Skip to content

Commit

Permalink
rename a few components; add pasting via web clipboard api
Browse files Browse the repository at this point in the history
  • Loading branch information
NaitLee committed Feb 22, 2024
1 parent a7f213e commit e241e08
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 51 deletions.
2 changes: 2 additions & 0 deletions common/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import IconArrowBarDown from "tabler_icons_tsx/tsx/arrow-bar-down.tsx";
import IconPrinter from "tabler_icons_tsx/tsx/printer.tsx";
import IconSettings from "tabler_icons_tsx/tsx/settings.tsx";
import IconMinus from "tabler_icons_tsx/tsx/minus.tsx";
import IconClipboardPlus from "tabler_icons_tsx/tsx/clipboard-plus.tsx";
import { INL_ICON_COLOR, INL_ICON_SIZE } from "./constants.ts";

export const Icons = {
Expand Down Expand Up @@ -57,6 +58,7 @@ export const Icons = {
IconPrinter,
IconSettings,
IconMinus,
IconClipboardPlus,
};

for (const key in Icons) {
Expand Down
7 changes: 5 additions & 2 deletions common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import type { Icons } from "./icons.tsx";
export type IconKey = keyof typeof Icons;

export interface StuffData {
type: 'void' | 'text' | 'hr' | 'border' | 'pic' | 'gap';
type: 'void' | 'text' | 'hr' | 'border' | 'pic' | 'gap' | 'qrcode';
id: number;

/* given after painting */
width?: number;
/* given after painting */
height?: number;

/* TODO: better way than such a trigger? */
triggerPaste?: boolean;

dither?: 'pic' | 'pattern' | 'text';
rotate?: 0 | 90 | 180 | 270;
flipH?: boolean;
Expand Down Expand Up @@ -53,7 +56,7 @@ export interface StuffUpdate {
stuff: StuffData;
}

export interface KittyCanvasProps {}
export interface KittyPrinterProps {}

export interface StuffPainterProps {
stuff: StuffData;
Expand Down
6 changes: 3 additions & 3 deletions components/Printer.tsx → components/Preview.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createRef } from "preact";
import { CAT_ADV_SRV, CAT_PRINT_RX_CHAR, CAT_PRINT_SRV, CAT_PRINT_TX_CHAR, DEF_CANVAS_WIDTH, DEF_ENERGY, DEF_FINISH_FEED, DEF_SPEED, STUFF_PAINT_INIT_URL } from "../common/constants.ts";
import { BitmapData, PrinterProps } from "../common/types.ts";
import StuffPainter from "./StuffPainter.tsx";
import StuffPreview from "./StuffPreview.tsx";
import { useMemo, useReducer } from "preact/hooks";
import { Icons } from "../common/icons.tsx";
import { _ } from "../common/i18n.tsx";
Expand Down Expand Up @@ -36,7 +36,7 @@ function rgbaToBits(data: Uint32Array) {
return result;
}

export default function Printer(props: PrinterProps) {
export default function Preview(props: PrinterProps) {
const [bitmap_data, dispatch] = useReducer<Record<number, BitmapData>, BitmapData>((data, update) => {
data[update.index] = update;
return data;
Expand All @@ -53,7 +53,7 @@ export default function Printer(props: PrinterProps) {
const preview = <div ref={preview_ref} class="kitty-preview">
{stuffs.map((stuff, index) =>
useMemo(() =>
<StuffPainter stuff={stuff} index={index} dispatch={dispatch} width={DEF_CANVAS_WIDTH} />
<StuffPreview stuff={stuff} index={index} dispatch={dispatch} width={DEF_CANVAS_WIDTH} />
, [JSON.stringify(stuff)])
)}
</div>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ declare function splitText(args: {

const image_worker = IS_BROWSER ? new Worker('/image_worker.js') : null;

export default function StuffPainter(props: StuffPainterProps) {
export default function StuffPreview(props: StuffPainterProps) {
const [imgsrc, set_imgsrc] = useState(STUFF_PAINT_INIT_URL);
if (!IS_BROWSER) return <img src={STUFF_PAINT_INIT_URL} width={props.width} height={1} />;
const { canvas, width, ctx, img } = mkcanvas(props.width);
Expand Down
100 changes: 70 additions & 30 deletions components/Stuff.tsx → components/StuffWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const commonsize = [
function nextsize(size: number) { return commonsize[commonsize.indexOf(size) + 1] || commonsize.at(-1); }
function prevsize(size: number) { return commonsize[commonsize.indexOf(size) - 1] || commonsize[0]; }

export default function Stuff(props: StuffProps) {
export default function StuffWidget(props: StuffProps) {
const [show_options, set_show_options] = useState(false);
const dispatch = props.dispatch;
const stuff = props.stuff;
Expand Down Expand Up @@ -46,37 +46,74 @@ export default function Stuff(props: StuffProps) {
}
}
const choose_pic = (event: Event) => {
const pic = event.currentTarget as HTMLImageElement;
const img = document.createElement('img');
// const pic = event.currentTarget as HTMLImageElement;
const selector = document.createElement('input');
selector.type = 'file';
selector.addEventListener('change', () => {
const file = selector.files![0];
if (!file) return;
img.src = URL.createObjectURL(file);
img.addEventListener('load', () => {
const canvas = document.createElement('canvas');
const ratio = img.width / img.height;
// downscale but keep sufficient resolution for both rotation
if (ratio >= 1) {
img.height = DEF_CANVAS_WIDTH;
img.width = DEF_CANVAS_WIDTH * ratio;
} else {
img.width = DEF_CANVAS_WIDTH;
img.height = DEF_CANVAS_WIDTH / ratio;
}
pic.width = canvas.width = img.width;
pic.height = canvas.height = img.height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0, img.width, img.height);
URL.revokeObjectURL(img.src);
stuff.picUrl = img.src = canvas.toDataURL();
// pic src updated automatically
dispatch({ action: 'modify', stuff: stuff });
}, { once: true });
const url = URL.createObjectURL(file);
use_pic(url).then(() => URL.revokeObjectURL(url));
}, { once: true });
selector.click();
}
const use_pic = (url: string) => new Promise<void>((resolve) => {
const img = document.createElement('img');
img.src = url;
img.addEventListener('load', () => {
const canvas = document.createElement('canvas');
const ratio = img.width / img.height;
// downscale but keep sufficient resolution for both rotation
if (ratio >= 1) {
img.height = DEF_CANVAS_WIDTH;
img.width = DEF_CANVAS_WIDTH * ratio;
} else {
img.width = DEF_CANVAS_WIDTH;
img.height = DEF_CANVAS_WIDTH / ratio;
}
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0, img.width, img.height);
stuff.picUrl = img.src = canvas.toDataURL();
// pic src updated automatically
dispatch({ action: 'modify', stuff: stuff });
resolve();
}, { once: true });
});
const paste = async () => {
const items = await navigator.clipboard.read().catch(() => []);
stuff.triggerPaste = false;
if (items.length === 0) return;
const first = items[0];
if (first.types.includes('text/plain')) {
const texts = [];
if (!stuff.textContent)
stuff.textContent = '';
if (stuff.textContent !== '' && !stuff.textContent.endsWith('\n'))
stuff.textContent += '\n';
for (const item of items)
if (item.types.includes('text/plain'))
texts.push(await item.getType('text/plain').then(b => b.text()));
if (!['text', 'qrcode'].includes(stuff.type)) {
stuff.type = 'text';
}
stuff.textContent += texts.join('\n');
dispatch({ action: 'modify', stuff: stuff });
} else if (first.types.includes('image/png')) {
const blob = await first.getType('image/png').catch(() => null);
if (blob === null) return;
if (!['pic'].includes(stuff.type)) {
stuff.type = 'pic';
stuff.dither = 'pic';
}
const url = URL.createObjectURL(blob);
use_pic(url).then(() => URL.revokeObjectURL(url));
}
};
if (stuff.triggerPaste) {
paste();
}
const change_type = (event: JSX.TargetedEvent<HTMLSelectElement, Event>) => {
const value = event.currentTarget.value as StuffData['type'];
stuff.type = value;
Expand Down Expand Up @@ -271,24 +308,27 @@ export default function Stuff(props: StuffProps) {
}
</div>
<div class="stuff__menu">
{show_options /* && stuff.id !== 0 */ ?
{show_options ? <>
<button key="moveup" class="stuff__button" aria-label={_("move-up")}
onClick={() => {
dispatch({ action: 'moveup', stuff: stuff });
set_show_options(false);
}}>
<Icons.IconArrowBarUp />
</button> : <></>
}
{show_options ?
</button>
<button key="movedown" class="stuff__button" aria-label={_("move-down")}
onClick={() => {
dispatch({ action: 'movedown', stuff: stuff });
set_show_options(false);
}}>
<Icons.IconArrowBarDown />
</button> : <></>
}
</button>
</> : <>
<button key="paste" class="stuff__button" aria-label={_("paste")}
onClick={paste}>
<Icons.IconClipboardPlus />
</button>
</>}
<button key="options" class="stuff__button" aria-label={_("options")}
onClick={() => set_show_options(!show_options)}>
<Icons.IconAdjustmentsHorizontal />
Expand Down
4 changes: 2 additions & 2 deletions fresh.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as $_app from "./routes/_app.tsx";
import * as $api_lang from "./routes/api/lang.ts";
import * as $index from "./routes/index.tsx";
import * as $DynamicManifest from "./islands/DynamicManifest.tsx";
import * as $KittyCanvas from "./islands/KittyCanvas.tsx";
import * as $KittyPrinter from "./islands/KittyPrinter.tsx";
import * as $Nav from "./islands/Nav.tsx";
import * as $PwaUpdate from "./islands/PwaUpdate.tsx";
import { type Manifest } from "$fresh/server.ts";
Expand All @@ -21,7 +21,7 @@ const manifest = {
},
islands: {
"./islands/DynamicManifest.tsx": $DynamicManifest,
"./islands/KittyCanvas.tsx": $KittyCanvas,
"./islands/KittyPrinter.tsx": $KittyPrinter,
"./islands/Nav.tsx": $Nav,
"./islands/PwaUpdate.tsx": $PwaUpdate,
},
Expand Down
27 changes: 16 additions & 11 deletions islands/KittyCanvas.tsx → islands/KittyPrinter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { useEffect, useReducer } from "preact/hooks";
import { DEF_PIC_URL, STUFF_STOREKEY } from "../common/constants.ts";
import { _, i18nReady } from "../common/i18n.tsx";
import { Icons } from "../common/icons.tsx";
import { KittyCanvasProps, StuffData, StuffUpdate } from "../common/types.ts";
import Printer from "../components/Printer.tsx";
import Stuff from "../components/Stuff.tsx";
import { KittyPrinterProps, StuffData, StuffUpdate } from "../common/types.ts";
import Preview from "../components/Preview.tsx";
import StuffWidget from "../components/StuffWidget.tsx";

function timestamp() {
return new Date().getTime();
Expand Down Expand Up @@ -45,7 +45,7 @@ function properStuff(stuff: StuffData) {
return stuff;
}

export default function KittyCanvas(props: KittyCanvasProps) {
export default function KittyPrinter(props: KittyPrinterProps) {
let stuff_store: StuffData[];
try {
//@ts-expect-error: many would go wrong, just reset in case
Expand Down Expand Up @@ -108,17 +108,22 @@ export default function KittyCanvas(props: KittyCanvasProps) {
{ type: 'text', id: 0, textContent: _('welcome').value, textAlign: 'center', textFontSize: 24 },
{ type: 'pic', id: 1, picUrl: DEF_PIC_URL }
];
initials.map(s => properStuff(s)).forEach(s => {
dispatch({
action: 'add',
stuff: s
});
initials.map(stuff => properStuff(stuff)).forEach(stuff => {
dispatch({ action: 'add', stuff: stuff });
});
});
});
useEffect(() => {
document.addEventListener('paste', () => {
dispatch({
action: 'add',
stuff: { type: 'text', id: 0, triggerPaste: true }
});
})
}, []);
const comp = <div class="kitty-container">
<div class="kitty-canvas">
{stuffs.map(stuff => Stuff({ dispatch, stuff }))}
{stuffs.map(stuff => StuffWidget({ dispatch, stuff }))}
<button class="stuff stuff--button" aria-label={_('add')} onClick={() => {
dispatch({
action: 'add',
Expand All @@ -129,7 +134,7 @@ export default function KittyCanvas(props: KittyCanvasProps) {
</button>
</div>
<div>
<Printer stuffs={stuffs} />
<Preview stuffs={stuffs} />
</div>
</div>;
return comp;
Expand Down
4 changes: 2 additions & 2 deletions routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Head } from "$fresh/runtime.ts";
import KittyCanvas from "../islands/KittyCanvas.tsx";
import KittyPrinter from "../islands/KittyPrinter.tsx";
import { _ } from "../common/i18n.tsx";
import Nav from "../islands/Nav.tsx";
import PwaUpdate from "../islands/PwaUpdate.tsx";
Expand All @@ -16,6 +16,6 @@ export default function Home(request: Request) {
</Head>
<DynamicManifest />
<Nav url={request.url} />
<KittyCanvas />
<KittyPrinter />
</>;
}
1 change: 1 addition & 0 deletions static/lang/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@
"TAB": "Tab",
"COMMA": "Comma",
"DOT": "Dot",
"paste": "Paste",
"": ""
}
1 change: 1 addition & 0 deletions static/lang/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@
"TAB": "Tab",
"COMMA": "逗号",
"DOT": "句号",
"paste": "粘贴",
"": ""
}
1 change: 1 addition & 0 deletions static/lang/zh-HK.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@
"TAB": "Tab",
"COMMA": "逗號",
"DOT": "句號",
"paste": "粘貼",
"": ""
}
1 change: 1 addition & 0 deletions static/lang/zh-Hant-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@
"TAB": "Tab",
"COMMA": "逗號",
"DOT": "句號",
"paste": "粘貼",
"": ""
}
1 change: 1 addition & 0 deletions static/lang/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@
"TAB": "Tab",
"COMMA": "逗號",
"DOT": "句號",
"paste": "貼上",
"": ""
}

0 comments on commit e241e08

Please sign in to comment.