kopia lustrzana https://github.com/zhengkyl/qrframe
lazy load editor + static build + update lucide
rodzic
541674b72b
commit
21fb3b4040
|
@ -1,29 +1,18 @@
|
||||||
import { fileURLToPath, URL } from "node:url";
|
|
||||||
import { defineConfig } from "@solidjs/start/config";
|
import { defineConfig } from "@solidjs/start/config";
|
||||||
import UnoCSS from "unocss/vite";
|
import UnoCSS from "unocss/vite";
|
||||||
import wasmpack from "vite-plugin-wasm-pack";
|
import wasmpack from "vite-plugin-wasm-pack";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
|
static: true,
|
||||||
preset: "cloudflare-pages",
|
preset: "cloudflare-pages",
|
||||||
rollupConfig: {
|
rollupConfig: {
|
||||||
external: ["node:async_hooks"]
|
external: ["node:async_hooks"],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
ssr: true,
|
ssr: false,
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [UnoCSS(), wasmpack([], ["fuqr"]), blobRewriter()],
|
plugins: [UnoCSS(), wasmpack([], ["fuqr"]), blobRewriter()],
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
// https://christopher.engineering/en/blog/lucide-icons-with-vite-dev-server/
|
|
||||||
"lucide-solid/icons": fileURLToPath(
|
|
||||||
new URL(
|
|
||||||
"./node_modules/lucide-solid/dist/source/icons",
|
|
||||||
import.meta.url
|
|
||||||
)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
"@unocss/reset": "^0.59.4",
|
"@unocss/reset": "^0.59.4",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"fuqr": "^1.0.0",
|
"fuqr": "^1.0.0",
|
||||||
"lucide-solid": "^0.378.0",
|
"lucide-solid": "^0.474.0",
|
||||||
"solid-js": "^1.9.2",
|
"solid-js": "^1.9.2",
|
||||||
"unocss": "^0.59.4",
|
"unocss": "^0.59.4",
|
||||||
"vinxi": "^0.3.11"
|
"vinxi": "^0.3.11"
|
||||||
|
|
|
@ -58,8 +58,8 @@ importers:
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
lucide-solid:
|
lucide-solid:
|
||||||
specifier: ^0.378.0
|
specifier: ^0.474.0
|
||||||
version: 0.378.0(solid-js@1.9.2)
|
version: 0.474.0(solid-js@1.9.2)
|
||||||
solid-js:
|
solid-js:
|
||||||
specifier: ^1.9.2
|
specifier: ^1.9.2
|
||||||
version: 1.9.2
|
version: 1.9.2
|
||||||
|
@ -2340,8 +2340,8 @@ packages:
|
||||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
lucide-solid@0.378.0:
|
lucide-solid@0.474.0:
|
||||||
resolution: {integrity: sha512-flJIh53qIVnHMsq4WipY+5dlclhnAIrVIIatK3TjUD+9aNNwvb8dSOYw5+Gyxdy69k0A4fWKmtU3k6O9AUdgLA==}
|
resolution: {integrity: sha512-sSEzUW2TVHpbYRehLfPuWtL+mGeBUQRZWPE0020aealCIxaNSuZOWts3NdS+R+itWr5JenTcwi1+Q3OcLTIZXg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
solid-js: ^1.4.7
|
solid-js: ^1.4.7
|
||||||
|
|
||||||
|
@ -5639,7 +5639,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
|
|
||||||
lucide-solid@0.378.0(solid-js@1.9.2):
|
lucide-solid@0.474.0(solid-js@1.9.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
solid-js: 1.9.2
|
solid-js: 1.9.2
|
||||||
|
|
||||||
|
@ -5737,7 +5737,7 @@ snapshots:
|
||||||
acorn: 8.11.3
|
acorn: 8.11.3
|
||||||
pathe: 1.1.2
|
pathe: 1.1.2
|
||||||
pkg-types: 1.1.0
|
pkg-types: 1.1.0
|
||||||
ufo: 1.5.3
|
ufo: 1.5.4
|
||||||
|
|
||||||
mri@1.2.0: {}
|
mri@1.2.0: {}
|
||||||
|
|
||||||
|
@ -5894,7 +5894,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
destr: 2.0.3
|
destr: 2.0.3
|
||||||
node-fetch-native: 1.6.4
|
node-fetch-native: 1.6.4
|
||||||
ufo: 1.5.3
|
ufo: 1.5.4
|
||||||
|
|
||||||
ohash@1.1.3: {}
|
ohash@1.1.3: {}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
declare module "lucide-solid/icons/*" {
|
|
||||||
import { LucideProps } from "lucide-solid/dist/types/types";
|
|
||||||
import { Component } from "solid-js";
|
|
||||||
const cmp: Component<LucideProps>;
|
|
||||||
|
|
||||||
export = cmp;
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@ export function Switch(props: Props) {
|
||||||
)}
|
)}
|
||||||
<KSwitch.Input class="peer" />
|
<KSwitch.Input class="peer" />
|
||||||
<KSwitch.Control class="inline-flex items-center w-11 h-6 px-0.5 bg-back-distinct data-[checked]:bg-fore-base transition-colors border rounded-3 peer-focus:(ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)">
|
<KSwitch.Control class="inline-flex items-center w-11 h-6 px-0.5 bg-back-distinct data-[checked]:bg-fore-base transition-colors border rounded-3 peer-focus:(ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)">
|
||||||
<KSwitch.Thumb class="h-5 w-5 rounded-2.5 bg-back-base data-[checked]:translate-x-[calc(100%-1px)] transition-transform" />
|
<KSwitch.Thumb class="h-5 w-5 rounded-2.5 bg-back-base data-[checked]:translate-x-[calc(100%-.125rem)] transition-transform" />
|
||||||
</KSwitch.Control>
|
</KSwitch.Control>
|
||||||
</KSwitch>
|
</KSwitch>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createEffect, createSignal, onMount, Show, untrack } from "solid-js";
|
import { createEffect, createSignal, onMount, untrack } from "solid-js";
|
||||||
|
|
||||||
import { basicSetup } from "codemirror";
|
import { basicSetup } from "codemirror";
|
||||||
import { historyKeymap, indentWithTab } from "@codemirror/commands";
|
import { historyKeymap, indentWithTab } from "@codemirror/commands";
|
||||||
|
@ -14,7 +14,6 @@ import { vim } from "@replit/codemirror-vim";
|
||||||
|
|
||||||
import { Button } from "@kobalte/core/button";
|
import { Button } from "@kobalte/core/button";
|
||||||
import { debounce } from "~/lib/util";
|
import { debounce } from "~/lib/util";
|
||||||
import { Switch } from "../Switch";
|
|
||||||
import { AllowPasteDialog } from "./AllowPasteDialog";
|
import { AllowPasteDialog } from "./AllowPasteDialog";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -26,7 +25,7 @@ const VIM_MODE_KEY = "vimMode";
|
||||||
const ALLOW_PASTE_KEY = "allowPaste";
|
const ALLOW_PASTE_KEY = "allowPaste";
|
||||||
|
|
||||||
export function CodeEditor(props: Props) {
|
export function CodeEditor(props: Props) {
|
||||||
let parent: HTMLDivElement;
|
let parent!: HTMLDivElement;
|
||||||
let view: EditorView;
|
let view: EditorView;
|
||||||
let modeComp = new Compartment();
|
let modeComp = new Compartment();
|
||||||
let allowPaste;
|
let allowPaste;
|
||||||
|
@ -140,58 +139,50 @@ export function CodeEditor(props: Props) {
|
||||||
view.contentDOM.blur();
|
view.contentDOM.blur();
|
||||||
});
|
});
|
||||||
|
|
||||||
const [showCode, setShowCode] = createSignal(false);
|
|
||||||
const [showDialog, setShowDialog] = createSignal(false);
|
const [showDialog, setShowDialog] = createSignal(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class="py-2">
|
<AllowPasteDialog
|
||||||
<Switch label="Code editor" value={showCode()} setValue={setShowCode} />
|
open={showDialog()}
|
||||||
</div>
|
setClosed={() => {
|
||||||
<div>
|
setShowDialog(false);
|
||||||
<AllowPasteDialog
|
}}
|
||||||
open={showDialog()}
|
onAllow={() => {
|
||||||
setClosed={() => {
|
allowPaste = true;
|
||||||
setShowDialog(false);
|
localStorage.setItem(ALLOW_PASTE_KEY, "true");
|
||||||
}}
|
}}
|
||||||
onAllow={() => {
|
/>
|
||||||
allowPaste = true;
|
<div class="flex justify-end gap-4 py-2">
|
||||||
localStorage.setItem(ALLOW_PASTE_KEY, "true");
|
<label class="flex items-center gap-1 text-sm">
|
||||||
}}
|
Vim mode
|
||||||
/>
|
<input
|
||||||
<div class="flex justify-end gap-4 pb-2 h-11">
|
class="h-4 w-4"
|
||||||
<Show when={showCode()}>
|
type="checkbox"
|
||||||
<label class="flex items-center gap-1 text-sm">
|
checked={vimMode()}
|
||||||
Vim mode
|
onChange={(e) => setVimMode(e.target.checked)}
|
||||||
<input
|
/>
|
||||||
class="h-4 w-4"
|
</label>
|
||||||
type="checkbox"
|
<label class="flex items-center gap-1 text-sm">
|
||||||
checked={vimMode()}
|
Update thumbnail
|
||||||
onChange={(e) => setVimMode(e.target.checked)}
|
<input
|
||||||
/>
|
class="h-4 w-4"
|
||||||
</label>
|
type="checkbox"
|
||||||
<label class="flex items-center gap-1 text-sm">
|
checked={updateThumbnail()}
|
||||||
Update thumbnail
|
onChange={(e) => setUpdateThumbnail(e.target.checked)}
|
||||||
<input
|
/>
|
||||||
class="h-4 w-4"
|
</label>
|
||||||
type="checkbox"
|
<Button
|
||||||
checked={updateThumbnail()}
|
disabled={!dirty()}
|
||||||
onChange={(e) => setUpdateThumbnail(e.target.checked)}
|
onMouseDown={() =>
|
||||||
/>
|
props.onSave(view.state.doc.toString(), updateThumbnail())
|
||||||
</label>
|
}
|
||||||
<Button
|
class="bg-green-700 border rounded-md hover:bg-green-700/90 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) disabled:(bg-transparent text-fore-base pointer-events-none opacity-50) transition-colors px-3 py-1 min-w-150px"
|
||||||
disabled={!dirty()}
|
>
|
||||||
onMouseDown={() =>
|
{dirty() ? "Save" : "No changes"}
|
||||||
props.onSave(view.state.doc.toString(), updateThumbnail())
|
</Button>
|
||||||
}
|
|
||||||
class="bg-green-700 border rounded-md hover:bg-green-700/90 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) disabled:(bg-transparent text-fore-base pointer-events-none opacity-50) transition-colors px-3 min-w-150px"
|
|
||||||
>
|
|
||||||
{dirty() ? "Save" : "No changes"}
|
|
||||||
</Button>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<div ref={parent!} classList={{ hidden: !showCode() }}></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div ref={parent!}></div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import {
|
import {
|
||||||
closestCenter, createSortable, DragDropProvider,
|
closestCenter,
|
||||||
|
createSortable,
|
||||||
|
DragDropProvider,
|
||||||
DragDropSensors,
|
DragDropSensors,
|
||||||
DragOverlay,
|
DragOverlay,
|
||||||
SortableProvider, transformStyle, useDragDropContext
|
SortableProvider,
|
||||||
|
transformStyle,
|
||||||
|
useDragDropContext,
|
||||||
} from "@thisbeyond/solid-dnd";
|
} from "@thisbeyond/solid-dnd";
|
||||||
import GripVertical from "lucide-solid/icons/grip-vertical";
|
import GripVertical from "lucide-solid/icons/grip-vertical";
|
||||||
import Minus from "lucide-solid/icons/minus";
|
import Minus from "lucide-solid/icons/minus";
|
||||||
|
@ -16,7 +20,7 @@ import { FlatButton } from "../Button";
|
||||||
export function ParamsEditor() {
|
export function ParamsEditor() {
|
||||||
const { paramsSchema, params, setParams } = useRenderContext();
|
const { paramsSchema, params, setParams } = useRenderContext();
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col gap-2 mb-4">
|
<div class="flex flex-col gap-2">
|
||||||
<For each={Object.entries(paramsSchema())}>
|
<For each={Object.entries(paramsSchema())}>
|
||||||
{([label, { type, ...other }]) => {
|
{([label, { type, ...other }]) => {
|
||||||
if (type === "array") {
|
if (type === "array") {
|
||||||
|
@ -125,7 +129,10 @@ function ArrayParam({ label, other }) {
|
||||||
value={v()}
|
value={v()}
|
||||||
setValue={(v: any) => setParams(label, i, v)}
|
setValue={(v: any) => setParams(label, i, v)}
|
||||||
/>
|
/>
|
||||||
<div class="px-1 cursor-move touch-none" {...sortable.dragActivators}>
|
<div
|
||||||
|
class="px-1 cursor-move touch-none"
|
||||||
|
{...sortable.dragActivators}
|
||||||
|
>
|
||||||
<GripVertical />
|
<GripVertical />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
import Pencil from "lucide-solid/icons/pencil";
|
import Pencil from "lucide-solid/icons/pencil";
|
||||||
import Trash2 from "lucide-solid/icons/trash-2";
|
import Trash2 from "lucide-solid/icons/trash-2";
|
||||||
import { For, Show, batch, createSignal, onMount, type JSX } from "solid-js";
|
import {
|
||||||
|
For,
|
||||||
|
Show,
|
||||||
|
Suspense,
|
||||||
|
batch,
|
||||||
|
createSignal,
|
||||||
|
lazy,
|
||||||
|
onMount,
|
||||||
|
type JSX,
|
||||||
|
} from "solid-js";
|
||||||
import { createStore } from "solid-js/store";
|
import { createStore } from "solid-js/store";
|
||||||
import {
|
import {
|
||||||
deepEqualObj,
|
deepEqualObj,
|
||||||
|
@ -17,9 +26,15 @@ import { Collapsible } from "../Collapsible";
|
||||||
import { ContentMenuTrigger, ContextMenuProvider } from "../ContextMenu";
|
import { ContentMenuTrigger, ContextMenuProvider } from "../ContextMenu";
|
||||||
import { ControlledDialog, DialogButton } from "../Dialog";
|
import { ControlledDialog, DialogButton } from "../Dialog";
|
||||||
import { TextInput, TextareaInput } from "../TextInput";
|
import { TextInput, TextareaInput } from "../TextInput";
|
||||||
import { CodeEditor } from "./CodeEditor";
|
|
||||||
import { ParamsEditor } from "./ParamsEditor";
|
import { ParamsEditor } from "./ParamsEditor";
|
||||||
import { Settings } from "./Settings";
|
import { Settings } from "./Settings";
|
||||||
|
import { Switch } from "../Switch";
|
||||||
|
|
||||||
|
const CodeEditor = lazy(() => {
|
||||||
|
return import("./CodeEditor").then((module) => ({
|
||||||
|
default: module.CodeEditor,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
import "virtual:blob-rewriter";
|
import "virtual:blob-rewriter";
|
||||||
|
|
||||||
|
@ -286,6 +301,8 @@ export function Editor(props: Props) {
|
||||||
saveAndRun(code, true, true);
|
saveAndRun(code, true, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [showCode, setShowCode] = createSignal(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={props.class}>
|
<div class={props.class}>
|
||||||
<TextareaInput
|
<TextareaInput
|
||||||
|
@ -310,7 +327,7 @@ export function Editor(props: Props) {
|
||||||
const [rename, setRename] = createSignal(key);
|
const [rename, setRename] = createSignal(key);
|
||||||
const [duplicate, setDuplicate] = createSignal(false);
|
const [duplicate, setDuplicate] = createSignal(false);
|
||||||
|
|
||||||
let ref: HTMLInputElement;
|
let ref!: HTMLInputElement;
|
||||||
onMount(() => ref.focus());
|
onMount(() => ref.focus());
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
|
@ -340,7 +357,7 @@ export function Editor(props: Props) {
|
||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
ref={ref!}
|
ref={ref}
|
||||||
defaultValue={rename()}
|
defaultValue={rename()}
|
||||||
onInput={(s) => {
|
onInput={(s) => {
|
||||||
if (duplicate()) setDuplicate(false);
|
if (duplicate()) setDuplicate(false);
|
||||||
|
@ -397,8 +414,8 @@ export function Editor(props: Props) {
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</ControlledDialog>
|
</ControlledDialog>
|
||||||
<div class="py-4">
|
<div class="py-4 flex flex-col gap-4">
|
||||||
<div class="mb-4 h-[180px] md:(h-unset)">
|
<div class="h-[180px] md:(h-unset)">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div class="text-sm py-2 border border-transparent">Presets</div>
|
<div class="text-sm py-2 border border-transparent">Presets</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
@ -429,7 +446,7 @@ export function Editor(props: Props) {
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-3 pt-2 pb-4 md:(flex-wrap static ml-0 px-0 overflow-x-visible) absolute max-w-full overflow-x-auto -ml-6 px-6">
|
<div class="flex gap-3 py-2 md:(flex-wrap static ml-0 px-0 overflow-x-visible) absolute max-w-full overflow-x-auto -ml-6 px-6">
|
||||||
<ContextMenuProvider
|
<ContextMenuProvider
|
||||||
disabled={isPreset(dialogKey())}
|
disabled={isPreset(dialogKey())}
|
||||||
onRename={() => setRenameOpen(true)}
|
onRename={() => setRenameOpen(true)}
|
||||||
|
@ -466,16 +483,27 @@ export function Editor(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ParamsEditor />
|
<ParamsEditor />
|
||||||
<CodeEditor
|
<div>
|
||||||
initialValue={code()}
|
<Switch
|
||||||
onSave={(code, updateThumbnail) => {
|
label="Code editor"
|
||||||
if (presetKeys.includes(renderKey())) {
|
value={showCode()}
|
||||||
createAndSelectFunc(renderKey(), code);
|
setValue={setShowCode}
|
||||||
} else {
|
/>
|
||||||
saveAndRun(code, true, updateThumbnail);
|
<Show when={showCode()}>
|
||||||
}
|
<Suspense fallback={<p>Loading...</p>}>
|
||||||
}}
|
<CodeEditor
|
||||||
/>
|
initialValue={code()}
|
||||||
|
onSave={(code, updateThumbnail) => {
|
||||||
|
if (presetKeys.includes(renderKey())) {
|
||||||
|
createAndSelectFunc(renderKey(), code);
|
||||||
|
} else {
|
||||||
|
saveAndRun(code, true, updateThumbnail);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -220,90 +220,87 @@ function DownloadButtons() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div class="flex gap-2 md:(grid grid-cols-2)">
|
||||||
<div class="font-bold text-sm pb-2 md:hidden">Downloads</div>
|
<SplitButton
|
||||||
<div class="flex gap-2 md:(grid grid-cols-2)">
|
disabled={disabled()}
|
||||||
<SplitButton
|
onPng={async (resizeWidth, resizeHeight) => {
|
||||||
disabled={disabled()}
|
try {
|
||||||
onPng={async (resizeWidth, resizeHeight) => {
|
const blob = await pngBlob(resizeWidth, resizeHeight);
|
||||||
try {
|
if (blob == null) throw "toBlob returned null";
|
||||||
const blob = await pngBlob(resizeWidth, resizeHeight);
|
|
||||||
if (blob == null) throw "toBlob returned null";
|
|
||||||
|
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
download(url, `${filename()}.png`);
|
download(url, `${filename()}.png`);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toastError("Failed to create image", e as string);
|
toastError("Failed to create image", e as string);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSvg={downloadSvg}
|
onSvg={downloadSvg}
|
||||||
/>
|
/>
|
||||||
<Show when={render()?.type === "svg"}>
|
<Show when={render()?.type === "svg"}>
|
||||||
<FlatButton
|
|
||||||
class="hidden md:inline-flex flex-1 justify-center items-center gap-1 px-3 py-2"
|
|
||||||
disabled={disabled()}
|
|
||||||
onClick={downloadSvg}
|
|
||||||
>
|
|
||||||
<Download size={20} />
|
|
||||||
SVG
|
|
||||||
</FlatButton>
|
|
||||||
</Show>
|
|
||||||
<FlatButton
|
<FlatButton
|
||||||
class="md:hidden inline-flex justify-center items-center gap-1 px-6 py-2"
|
class="hidden md:inline-flex flex-1 justify-center items-center gap-1 px-3 py-2"
|
||||||
disabled={disabled()}
|
disabled={disabled()}
|
||||||
title="Share"
|
onClick={downloadSvg}
|
||||||
onClick={async () => {
|
|
||||||
let blob;
|
|
||||||
try {
|
|
||||||
blob = await pngBlob(0, 0);
|
|
||||||
if (blob == null) throw "toBlob returned null";
|
|
||||||
} catch (e) {
|
|
||||||
toastError(
|
|
||||||
"Failed to create image",
|
|
||||||
typeof e === "string" ? e : "pngBlob failed"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const shareData = {
|
|
||||||
files: [
|
|
||||||
new File([blob], `${filename()}.png`, {
|
|
||||||
type: "image/png",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
if (!navigator.canShare(shareData)) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
navigator.share(shareData);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
toastError(
|
|
||||||
"Native sharing failed",
|
|
||||||
"File sharing not supported by browser"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Share2 size={20} />
|
<Download size={20} />
|
||||||
|
SVG
|
||||||
</FlatButton>
|
</FlatButton>
|
||||||
<Popover gutter={4}>
|
</Show>
|
||||||
<Popover.Trigger
|
<FlatButton
|
||||||
class="md:hidden border rounded-md hover:bg-fore-base/5 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) p-2 disabled:(pointer-events-none opacity-50)"
|
class="md:hidden inline-flex justify-center items-center gap-1 px-6 py-2"
|
||||||
disabled={disabled()}
|
disabled={disabled()}
|
||||||
title="QR Metadata"
|
title="Share"
|
||||||
>
|
onClick={async () => {
|
||||||
<Info size={20} />
|
let blob;
|
||||||
</Popover.Trigger>
|
try {
|
||||||
<Popover.Portal>
|
blob = await pngBlob(0, 0);
|
||||||
<Popover.Content class="z-50 bg-back-base rounded-md border p-2 outline-none min-w-150px leading-tight">
|
if (blob == null) throw "toBlob returned null";
|
||||||
<Metadata />
|
} catch (e) {
|
||||||
</Popover.Content>
|
toastError(
|
||||||
</Popover.Portal>
|
"Failed to create image",
|
||||||
</Popover>
|
typeof e === "string" ? e : "pngBlob failed"
|
||||||
</div>
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const shareData = {
|
||||||
|
files: [
|
||||||
|
new File([blob], `${filename()}.png`, {
|
||||||
|
type: "image/png",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
if (!navigator.canShare(shareData)) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
navigator.share(shareData);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
toastError(
|
||||||
|
"Native sharing failed",
|
||||||
|
"File sharing not supported by browser"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Share2 size={20} />
|
||||||
|
</FlatButton>
|
||||||
|
<Popover gutter={4}>
|
||||||
|
<Popover.Trigger
|
||||||
|
class="md:hidden border rounded-md hover:bg-fore-base/5 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) p-2 disabled:(pointer-events-none opacity-50)"
|
||||||
|
disabled={disabled()}
|
||||||
|
title="QR Metadata"
|
||||||
|
>
|
||||||
|
<Info size={20} />
|
||||||
|
</Popover.Trigger>
|
||||||
|
<Popover.Portal>
|
||||||
|
<Popover.Content class="z-50 bg-back-base rounded-md border p-2 outline-none min-w-150px leading-tight">
|
||||||
|
<Metadata />
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Portal>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue