Skip to content

Commit

Permalink
Merge pull request #18 from projectstorm/feature_rename
Browse files Browse the repository at this point in the history
Rename boards, page scrolling and dialog style fixes in light mode
  • Loading branch information
dylanvorster authored Jul 2, 2023
2 parents 3f9511f + ed7967e commit a6193d0
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 18 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Concept image-board software for ambitious creatives 🎨
## What

Tornado is self-hosted software for the web (currently in development) that provides digital media artists with the ability to create concept and reference boards.
You can simply drag in images, or paste images you have copied in your clipboard, and then arrange them as you see fit.
You can simply paste images you have copied in your clipboard, and then arrange them as you see fit.


## Features:
Expand All @@ -22,6 +22,7 @@ You can simply drag in images, or paste images you have copied in your clipboard
* Image resizing on the server
* Canvas zoom and translate
* Image paste from clipboard and translate
* Name and rename boards

![](./images/screenshot.png)
![](./images/screenshot2.png)
Expand Down
4 changes: 4 additions & 0 deletions tornado-frontend/src/System.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export class System {
makeObservable(this);
}

updateTitle(title: string) {
document.title = `Tornado${title ? ` | ${title}` : ''}`;
}

toggleTheme() {
if (this.theme.light) {
this.theme = ThemeDark;
Expand Down
2 changes: 1 addition & 1 deletion tornado-frontend/src/hooks/useButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ export const useButton = (props: UseButtonOptions, ref: React.RefObject<HTMLDivE
return () => {
ref.current?.removeEventListener('click', l);
};
}, []);
}, [props.action, props.disabled]);
return { loading };
};
3 changes: 1 addition & 2 deletions tornado-frontend/src/routes/content/ConceptBoardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useEffect } from 'react';
import styled from '@emotion/styled';
import { useAuthenticated } from '../../hooks/useAuthenticated';
import { ContentViewWidget } from './widgets/ContentViewWidget';
import { usePasteMedia } from '../../hooks/usePasteMedia';
import { useSystem } from '../../hooks/useSystem';
import { ConceptCanvasWidget } from './widgets/react-canvas/ConceptCanvasWidget';
import { useParams } from 'react-router-dom';
Expand All @@ -21,7 +20,7 @@ export const ConceptBoardPage: React.FC<ConceptBoardPageProps> = observer((props
return autorun(() => {
const concept = system.conceptStore.getConcept(parseInt(id));
if (concept) {
concept.loadData();
system.updateTitle(`Concept ${concept.board.name}`);
}
});
}, [id]);
Expand Down
25 changes: 24 additions & 1 deletion tornado-frontend/src/routes/manage/ConceptBoardsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const ConceptBoardsPage: React.FC = observer((props) => {
const system = useSystem();
useEffect(() => {
system.conceptStore.loadConcepts();
system.updateTitle('Concepts');
}, []);
return (
<S.Container>
Expand Down Expand Up @@ -51,7 +52,21 @@ export const ConceptBoardsPage: React.FC = observer((props) => {
render: ({ rowHover, row }) => {
return (
<TableRowActionsWidget show={rowHover}>
<ButtonWidget
<S.RowButton
label="Rename"
type={ButtonType.NORMAL}
action={async () => {
const name = await system.dialogStore.showInputDialog({
title: 'Enter a new name',
desc: `You are about to rename concept board ${row.board.board.name}`
});
if (!name) {
return;
}
return row.board.setName(name);
}}
/>
<S.RowButton
label="Delete"
type={ButtonType.NORMAL}
action={async () => {
Expand Down Expand Up @@ -96,4 +111,12 @@ namespace S {
export const Buttons = styled.div`
margin-bottom: 5px;
`;

export const RowButton = styled(ButtonWidget)`
margin-right: 5px;
&:last-of-type {
margin-right: 0;
}
`;
}
11 changes: 6 additions & 5 deletions tornado-frontend/src/stores/ConceptsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ export class ConceptBoardModel extends BaseObserver<ConceptBoardModelListener> {
});
}

updateBoard(board: ConceptBoard) {
this.board = board;
async setName(name: string) {
this.board.name = name;
await this.options.client.updateConcept({
board: this.board
});
}

get id() {
Expand All @@ -45,8 +48,6 @@ export class ConceptBoardModel extends BaseObserver<ConceptBoardModelListener> {
async delete() {
return this.iterateListenersAsync((cb) => cb.deleted?.());
}

async loadData() {}
}

export class ConceptsStore {
Expand All @@ -69,7 +70,7 @@ export class ConceptsStore {
});
return board;
},
update: (c, board) => board.updateBoard(c)
update: (c, board) => (board.board = c)
});
}

Expand Down
40 changes: 39 additions & 1 deletion tornado-frontend/src/stores/DialogStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@ import * as React from 'react';
import { Layer, LayerStore } from './LayerStore';
import { DialogWidget } from '../widgets/dialog/DialogWidget';
import { ButtonType } from '../widgets/forms/ButtonWidget';
import { InputDialogWidget } from '../widgets/dialog/InputDialogWidget';
import { Simulate } from 'react-dom/test-utils';
import submit = Simulate.submit;

export interface DialogStoreOptions {
layerStore: LayerStore;
}

export interface CommonDialogOptions {
title: string;
desc: string;
}

export class DialogStore {
constructor(protected options: DialogStoreOptions) {}

async showConfirmDialog(options: { title: string; desc: string }): Promise<boolean> {
async showConfirmDialog(options: CommonDialogOptions): Promise<boolean> {
return await new Promise<boolean>((resolve) => {
let confirm = false;
const layer = new Layer(() => {
Expand Down Expand Up @@ -48,4 +56,34 @@ export class DialogStore {
this.options.layerStore.addLayer(layer);
});
}

async showInputDialog(options: CommonDialogOptions & { initial?: string }): Promise<string | null> {
return await new Promise<string>((resolve) => {
let value: string = null;
const layer = new Layer(() => {
return (
<InputDialogWidget
title={options.title}
desc={options.desc}
submit={(val) => {
value = val;
layer.dispose();
}}
cancel={() => {
layer.dispose();
}}
initial={options.initial || ''}
/>
);
});
const l1 = layer.registerListener({
disposed: () => {
l1();
resolve(value);
}
});

this.options.layerStore.addLayer(layer);
});
}
}
12 changes: 6 additions & 6 deletions tornado-frontend/src/theme/theme-light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ export const ThemeLight: TornadoTheme = {
centerPanel: '#d2d2d2'
},
dialog: {
background: '#131315',
border: '#000',
header: '#fff',
desc: '#5f5f60',
shadow: 'rgba(0,0,0,0.3)'
background: '#eaeaea',
border: '#b6b6b6',
header: '#000000',
desc: '#424242',
shadow: 'rgba(122,122,122,0.3)'
},
text: {
heading: '#000000',
Expand All @@ -34,7 +34,7 @@ export const ThemeLight: TornadoTheme = {
controls: {
error: '#ab3838',
field: {
background: '#c4c4c4',
background: '#e1e1e1',
color: '#000000',
placeholder: '#616162'
},
Expand Down
45 changes: 45 additions & 0 deletions tornado-frontend/src/widgets/dialog/InputDialogWidget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as React from 'react';
import { useState } from 'react';
import { ButtonType } from '../forms/ButtonWidget';
import { DialogWidget, DialogWidgetProps } from './DialogWidget';
import { FieldWidget } from '../forms/FieldWidget';

export interface InputDialogWidgetProps extends Omit<DialogWidgetProps, 'btns'> {
initial?: string;
submit: (value: string) => any;
cancel: () => any;
}

export const InputDialogWidget: React.FC<InputDialogWidgetProps> = (props) => {
const [value, setValue] = useState(props.initial);

return (
<DialogWidget
{...props}
btns={[
{
label: 'Submit',
action: async () => {
props.submit(value);
},
type: ButtonType.PRIMARY
},
{
label: 'Cancel',
action: async () => {
props.cancel();
},
type: ButtonType.NORMAL
}
]}
>
<FieldWidget
value={value}
onChange={(v) => {
setValue(v);
}}
placeholder=""
/>
</DialogWidget>
);
};
2 changes: 1 addition & 1 deletion tornado-frontend/src/widgets/forms/FieldWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const FieldWidget: React.FC<FieldWidgetProps> = (props) => {
ref={props.forwardRef}
placeholder={props.placeholder}
type={props.type}
value={props.value?.trim() || ''}
value={props.value || ''}
onChange={(event) => {
let value = event.target.value;
if (value.trim() === '') {
Expand Down
1 change: 1 addition & 0 deletions tornado-frontend/src/widgets/layout/RootWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace S {

export const Body = styled(BodyWidget)`
flex-grow: 1;
overflow-y: auto;
`;

export const Layers = styled(LayersWidget)`
Expand Down

0 comments on commit a6193d0

Please sign in to comment.