@vizel/svelte 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -0
- package/dist/components/Vizel.svelte +158 -0
- package/dist/components/Vizel.svelte.d.ts +82 -0
- package/dist/components/Vizel.svelte.d.ts.map +1 -0
- package/dist/components/VizelBubbleMenu.svelte +96 -0
- package/dist/components/VizelBubbleMenu.svelte.d.ts +28 -0
- package/dist/components/VizelBubbleMenu.svelte.d.ts.map +1 -0
- package/dist/components/VizelBubbleMenuButton.svelte +45 -0
- package/dist/components/VizelBubbleMenuButton.svelte.d.ts +21 -0
- package/dist/components/VizelBubbleMenuButton.svelte.d.ts.map +1 -0
- package/dist/components/VizelBubbleMenuColorPicker.svelte +135 -0
- package/dist/components/VizelBubbleMenuColorPicker.svelte.d.ts +19 -0
- package/dist/components/VizelBubbleMenuColorPicker.svelte.d.ts.map +1 -0
- package/dist/components/VizelBubbleMenuDefault.svelte +111 -0
- package/dist/components/VizelBubbleMenuDefault.svelte.d.ts +13 -0
- package/dist/components/VizelBubbleMenuDefault.svelte.d.ts.map +1 -0
- package/dist/components/VizelBubbleMenuDivider.svelte +12 -0
- package/dist/components/VizelBubbleMenuDivider.svelte.d.ts +8 -0
- package/dist/components/VizelBubbleMenuDivider.svelte.d.ts.map +1 -0
- package/dist/components/VizelColorPicker.svelte +269 -0
- package/dist/components/VizelColorPicker.svelte.d.ts +25 -0
- package/dist/components/VizelColorPicker.svelte.d.ts.map +1 -0
- package/dist/components/VizelContext.d.ts +27 -0
- package/dist/components/VizelContext.d.ts.map +1 -0
- package/dist/components/VizelContext.js +34 -0
- package/dist/components/VizelEditor.svelte +61 -0
- package/dist/components/VizelEditor.svelte.d.ts +17 -0
- package/dist/components/VizelEditor.svelte.d.ts.map +1 -0
- package/dist/components/VizelEmbedView.svelte +162 -0
- package/dist/components/VizelEmbedView.svelte.d.ts +13 -0
- package/dist/components/VizelEmbedView.svelte.d.ts.map +1 -0
- package/dist/components/VizelIcon.svelte +54 -0
- package/dist/components/VizelIcon.svelte.d.ts +32 -0
- package/dist/components/VizelIcon.svelte.d.ts.map +1 -0
- package/dist/components/VizelIconContext.d.ts +27 -0
- package/dist/components/VizelIconContext.d.ts.map +1 -0
- package/dist/components/VizelIconContext.js +31 -0
- package/dist/components/VizelIconProvider.svelte +43 -0
- package/dist/components/VizelIconProvider.svelte.d.ts +31 -0
- package/dist/components/VizelIconProvider.svelte.d.ts.map +1 -0
- package/dist/components/VizelLinkEditor.svelte +143 -0
- package/dist/components/VizelLinkEditor.svelte.d.ts +15 -0
- package/dist/components/VizelLinkEditor.svelte.d.ts.map +1 -0
- package/dist/components/VizelNodeSelector.svelte +172 -0
- package/dist/components/VizelNodeSelector.svelte.d.ts +13 -0
- package/dist/components/VizelNodeSelector.svelte.d.ts.map +1 -0
- package/dist/components/VizelPortal.svelte +70 -0
- package/dist/components/VizelPortal.svelte.d.ts +19 -0
- package/dist/components/VizelPortal.svelte.d.ts.map +1 -0
- package/dist/components/VizelProvider.svelte +26 -0
- package/dist/components/VizelProvider.svelte.d.ts +14 -0
- package/dist/components/VizelProvider.svelte.d.ts.map +1 -0
- package/dist/components/VizelSaveIndicator.svelte +94 -0
- package/dist/components/VizelSaveIndicator.svelte.d.ts +15 -0
- package/dist/components/VizelSaveIndicator.svelte.d.ts.map +1 -0
- package/dist/components/VizelSlashMenu.svelte +211 -0
- package/dist/components/VizelSlashMenu.svelte.d.ts +31 -0
- package/dist/components/VizelSlashMenu.svelte.d.ts.map +1 -0
- package/dist/components/VizelSlashMenuEmpty.svelte +22 -0
- package/dist/components/VizelSlashMenuEmpty.svelte.d.ts +11 -0
- package/dist/components/VizelSlashMenuEmpty.svelte.d.ts.map +1 -0
- package/dist/components/VizelSlashMenuItem.svelte +57 -0
- package/dist/components/VizelSlashMenuItem.svelte.d.ts +17 -0
- package/dist/components/VizelSlashMenuItem.svelte.d.ts.map +1 -0
- package/dist/components/VizelThemeProvider.svelte +79 -0
- package/dist/components/VizelThemeProvider.svelte.d.ts +11 -0
- package/dist/components/VizelThemeProvider.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +23 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +64 -0
- package/dist/iconRenderer.d.ts +6 -0
- package/dist/iconRenderer.d.ts.map +1 -0
- package/dist/iconRenderer.js +7 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/runes/createVizelAutoSave.svelte.d.ts +44 -0
- package/dist/runes/createVizelAutoSave.svelte.d.ts.map +1 -0
- package/dist/runes/createVizelAutoSave.svelte.js +91 -0
- package/dist/runes/createVizelEditor.svelte.d.ts +43 -0
- package/dist/runes/createVizelEditor.svelte.d.ts.map +1 -0
- package/dist/runes/createVizelEditor.svelte.js +65 -0
- package/dist/runes/createVizelEditorState.svelte.d.ts +27 -0
- package/dist/runes/createVizelEditorState.svelte.d.ts.map +1 -0
- package/dist/runes/createVizelEditorState.svelte.js +35 -0
- package/dist/runes/createVizelMarkdown.svelte.d.ts +68 -0
- package/dist/runes/createVizelMarkdown.svelte.d.ts.map +1 -0
- package/dist/runes/createVizelMarkdown.svelte.js +123 -0
- package/dist/runes/createVizelSlashMenuRenderer.d.ts +22 -0
- package/dist/runes/createVizelSlashMenuRenderer.d.ts.map +1 -0
- package/dist/runes/createVizelSlashMenuRenderer.js +84 -0
- package/dist/runes/createVizelState.svelte.d.ts +22 -0
- package/dist/runes/createVizelState.svelte.d.ts.map +1 -0
- package/dist/runes/createVizelState.svelte.js +50 -0
- package/dist/runes/getVizelTheme.svelte.d.ts +23 -0
- package/dist/runes/getVizelTheme.svelte.d.ts.map +1 -0
- package/dist/runes/getVizelTheme.svelte.js +31 -0
- package/dist/runes/index.d.ts +8 -0
- package/dist/runes/index.d.ts.map +1 -0
- package/dist/runes/index.js +7 -0
- package/package.json +64 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Editor, VizelNodeTypeOption } from "@vizel/core";
|
|
3
|
+
|
|
4
|
+
export interface VizelNodeSelectorProps {
|
|
5
|
+
/** The editor instance */
|
|
6
|
+
editor: Editor;
|
|
7
|
+
/** Custom node types (defaults to vizelDefaultNodeTypes) */
|
|
8
|
+
nodeTypes?: VizelNodeTypeOption[];
|
|
9
|
+
/** Custom class name */
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import { vizelDefaultNodeTypes, getVizelActiveNodeType } from "@vizel/core";
|
|
16
|
+
import { createVizelState } from "../runes/createVizelState.svelte.ts";
|
|
17
|
+
import VizelIcon from "./VizelIcon.svelte";
|
|
18
|
+
|
|
19
|
+
let { editor, nodeTypes = vizelDefaultNodeTypes, class: className }: VizelNodeSelectorProps = $props();
|
|
20
|
+
|
|
21
|
+
// Subscribe to editor state changes
|
|
22
|
+
const editorState = createVizelState(() => editor);
|
|
23
|
+
|
|
24
|
+
let isOpen = $state(false);
|
|
25
|
+
let focusedIndex = $state(0);
|
|
26
|
+
let containerRef: HTMLDivElement | undefined = $state();
|
|
27
|
+
let dropdownRef: HTMLDivElement | undefined = $state();
|
|
28
|
+
|
|
29
|
+
const activeNodeType = $derived.by(() => {
|
|
30
|
+
void editorState.current; // Trigger reactivity
|
|
31
|
+
return getVizelActiveNodeType(editor, nodeTypes);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const currentLabel = $derived(activeNodeType?.label ?? "Text");
|
|
35
|
+
const currentIcon = $derived(activeNodeType?.icon ?? "paragraph");
|
|
36
|
+
|
|
37
|
+
// Close dropdown when clicking outside
|
|
38
|
+
function handleClickOutside(event: MouseEvent) {
|
|
39
|
+
if (containerRef && !containerRef.contains(event.target as Node)) {
|
|
40
|
+
isOpen = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
$effect(() => {
|
|
45
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
46
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Focus the dropdown when it opens to ensure keyboard navigation works
|
|
50
|
+
$effect(() => {
|
|
51
|
+
if (isOpen && dropdownRef) {
|
|
52
|
+
dropdownRef.focus();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
57
|
+
if (!isOpen) {
|
|
58
|
+
if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
isOpen = true;
|
|
61
|
+
focusedIndex = 0;
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
switch (event.key) {
|
|
67
|
+
case "Escape":
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
isOpen = false;
|
|
70
|
+
break;
|
|
71
|
+
case "ArrowDown":
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
focusedIndex = (focusedIndex + 1) % nodeTypes.length;
|
|
74
|
+
break;
|
|
75
|
+
case "ArrowUp":
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
focusedIndex = (focusedIndex - 1 + nodeTypes.length) % nodeTypes.length;
|
|
78
|
+
break;
|
|
79
|
+
case "Enter":
|
|
80
|
+
case " ": {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
const selectedNodeType = nodeTypes[focusedIndex];
|
|
83
|
+
if (selectedNodeType) {
|
|
84
|
+
handleSelectNodeType(selectedNodeType);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case "Home":
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
focusedIndex = 0;
|
|
91
|
+
break;
|
|
92
|
+
case "End":
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
focusedIndex = nodeTypes.length - 1;
|
|
95
|
+
break;
|
|
96
|
+
default:
|
|
97
|
+
// Allow other keys to propagate
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function handleSelectNodeType(nodeType: VizelNodeTypeOption) {
|
|
103
|
+
nodeType.command(editor);
|
|
104
|
+
isOpen = false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isNodeTypeActive(nodeType: VizelNodeTypeOption): boolean {
|
|
108
|
+
void editorState.current; // Trigger reactivity
|
|
109
|
+
return nodeType.isActive(editor);
|
|
110
|
+
}
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<div
|
|
114
|
+
bind:this={containerRef}
|
|
115
|
+
class="vizel-node-selector {className ?? ''}"
|
|
116
|
+
data-vizel-node-selector
|
|
117
|
+
>
|
|
118
|
+
<button
|
|
119
|
+
type="button"
|
|
120
|
+
class="vizel-node-selector-trigger"
|
|
121
|
+
aria-haspopup="listbox"
|
|
122
|
+
aria-expanded={isOpen}
|
|
123
|
+
aria-label={`Current block type: ${currentLabel}`}
|
|
124
|
+
title="Change block type"
|
|
125
|
+
onclick={() => (isOpen = !isOpen)}
|
|
126
|
+
onkeydown={handleKeyDown}
|
|
127
|
+
>
|
|
128
|
+
<span class="vizel-node-selector-icon">
|
|
129
|
+
<VizelIcon name={currentIcon} />
|
|
130
|
+
</span>
|
|
131
|
+
<span class="vizel-node-selector-label">{currentLabel}</span>
|
|
132
|
+
<span class="vizel-node-selector-chevron" aria-hidden="true">
|
|
133
|
+
<VizelIcon name="chevronDown" />
|
|
134
|
+
</span>
|
|
135
|
+
</button>
|
|
136
|
+
|
|
137
|
+
{#if isOpen}
|
|
138
|
+
<div
|
|
139
|
+
bind:this={dropdownRef}
|
|
140
|
+
class="vizel-node-selector-dropdown"
|
|
141
|
+
role="listbox"
|
|
142
|
+
aria-label="Block types"
|
|
143
|
+
data-vizel-node-selector-dropdown
|
|
144
|
+
tabindex="-1"
|
|
145
|
+
onkeydown={handleKeyDown}
|
|
146
|
+
>
|
|
147
|
+
{#each nodeTypes as nodeType, index}
|
|
148
|
+
{@const active = isNodeTypeActive(nodeType)}
|
|
149
|
+
{@const focused = index === focusedIndex}
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
role="option"
|
|
153
|
+
aria-selected={active}
|
|
154
|
+
class="vizel-node-selector-option {active ? 'is-active' : ''} {focused ? 'is-focused' : ''}"
|
|
155
|
+
tabindex={focused ? 0 : -1}
|
|
156
|
+
onclick={() => handleSelectNodeType(nodeType)}
|
|
157
|
+
onmouseenter={() => (focusedIndex = index)}
|
|
158
|
+
>
|
|
159
|
+
<span class="vizel-node-selector-option-icon">
|
|
160
|
+
<VizelIcon name={nodeType.icon} />
|
|
161
|
+
</span>
|
|
162
|
+
<span class="vizel-node-selector-option-label">{nodeType.label}</span>
|
|
163
|
+
{#if active}
|
|
164
|
+
<span class="vizel-node-selector-check" aria-hidden="true">
|
|
165
|
+
<VizelIcon name="check" />
|
|
166
|
+
</span>
|
|
167
|
+
{/if}
|
|
168
|
+
</button>
|
|
169
|
+
{/each}
|
|
170
|
+
</div>
|
|
171
|
+
{/if}
|
|
172
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Editor, VizelNodeTypeOption } from "@vizel/core";
|
|
2
|
+
export interface VizelNodeSelectorProps {
|
|
3
|
+
/** The editor instance */
|
|
4
|
+
editor: Editor;
|
|
5
|
+
/** Custom node types (defaults to vizelDefaultNodeTypes) */
|
|
6
|
+
nodeTypes?: VizelNodeTypeOption[];
|
|
7
|
+
/** Custom class name */
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const VizelNodeSelector: import("svelte").Component<VizelNodeSelectorProps, {}, "">;
|
|
11
|
+
type VizelNodeSelector = ReturnType<typeof VizelNodeSelector>;
|
|
12
|
+
export default VizelNodeSelector;
|
|
13
|
+
//# sourceMappingURL=VizelNodeSelector.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VizelNodeSelector.svelte.d.ts","sourceRoot":"","sources":["../../src/components/VizelNodeSelector.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACrC,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA8ID,QAAA,MAAM,iBAAiB,4DAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { VizelPortalLayer } from "@vizel/core";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Props for the Portal component.
|
|
7
|
+
*/
|
|
8
|
+
export interface VizelPortalProps {
|
|
9
|
+
/** Content to render in the portal */
|
|
10
|
+
children: Snippet;
|
|
11
|
+
/** Z-index layer for the portal content */
|
|
12
|
+
layer?: VizelPortalLayer;
|
|
13
|
+
/** Additional CSS class name */
|
|
14
|
+
class?: string;
|
|
15
|
+
/** Whether the portal is disabled (renders children in place) */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import { getVizelPortalContainer, VIZEL_PORTAL_Z_INDEX } from "@vizel/core";
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
children,
|
|
25
|
+
layer = "dropdown",
|
|
26
|
+
class: className,
|
|
27
|
+
disabled = false,
|
|
28
|
+
}: VizelPortalProps = $props();
|
|
29
|
+
|
|
30
|
+
// Create portal action that moves content to the portal container
|
|
31
|
+
function portal(node: HTMLElement) {
|
|
32
|
+
const container = getVizelPortalContainer();
|
|
33
|
+
|
|
34
|
+
// Create wrapper element
|
|
35
|
+
const wrapper = document.createElement("div");
|
|
36
|
+
wrapper.setAttribute("data-vizel-portal-layer", layer);
|
|
37
|
+
if (className) {
|
|
38
|
+
wrapper.className = className;
|
|
39
|
+
}
|
|
40
|
+
wrapper.style.position = "absolute";
|
|
41
|
+
wrapper.style.top = "0";
|
|
42
|
+
wrapper.style.left = "0";
|
|
43
|
+
wrapper.style.zIndex = String(VIZEL_PORTAL_Z_INDEX[layer]);
|
|
44
|
+
|
|
45
|
+
// Move node's children to wrapper
|
|
46
|
+
while (node.firstChild) {
|
|
47
|
+
wrapper.appendChild(node.firstChild);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
container.appendChild(wrapper);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
destroy() {
|
|
54
|
+
// Move content back to original location for cleanup
|
|
55
|
+
while (wrapper.firstChild) {
|
|
56
|
+
node.appendChild(wrapper.firstChild);
|
|
57
|
+
}
|
|
58
|
+
wrapper.remove();
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
{#if disabled}
|
|
65
|
+
{@render children()}
|
|
66
|
+
{:else}
|
|
67
|
+
<div use:portal>
|
|
68
|
+
{@render children()}
|
|
69
|
+
</div>
|
|
70
|
+
{/if}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VizelPortalLayer } from "@vizel/core";
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
/**
|
|
4
|
+
* Props for the Portal component.
|
|
5
|
+
*/
|
|
6
|
+
export interface VizelPortalProps {
|
|
7
|
+
/** Content to render in the portal */
|
|
8
|
+
children: Snippet;
|
|
9
|
+
/** Z-index layer for the portal content */
|
|
10
|
+
layer?: VizelPortalLayer;
|
|
11
|
+
/** Additional CSS class name */
|
|
12
|
+
class?: string;
|
|
13
|
+
/** Whether the portal is disabled (renders children in place) */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
declare const VizelPortal: import("svelte").Component<VizelPortalProps, {}, "">;
|
|
17
|
+
type VizelPortal = ReturnType<typeof VizelPortal>;
|
|
18
|
+
export default VizelPortal;
|
|
19
|
+
//# sourceMappingURL=VizelPortal.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VizelPortal.svelte.d.ts","sourceRoot":"","sources":["../../src/components/VizelPortal.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA6DD,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Editor } from "@vizel/core";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
|
|
5
|
+
export interface VizelProviderProps {
|
|
6
|
+
/** The editor instance */
|
|
7
|
+
editor: Editor | null;
|
|
8
|
+
/** Custom class name */
|
|
9
|
+
class?: string;
|
|
10
|
+
/** Children content */
|
|
11
|
+
children: Snippet;
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import { setContext } from "svelte";
|
|
17
|
+
import { VIZEL_CONTEXT_KEY } from "./VizelContext.ts";
|
|
18
|
+
|
|
19
|
+
let { editor, class: className, children }: VizelProviderProps = $props();
|
|
20
|
+
|
|
21
|
+
setContext(VIZEL_CONTEXT_KEY, () => editor);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div class={className} data-vizel-root>
|
|
25
|
+
{@render children()}
|
|
26
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Editor } from "@vizel/core";
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
export interface VizelProviderProps {
|
|
4
|
+
/** The editor instance */
|
|
5
|
+
editor: Editor | null;
|
|
6
|
+
/** Custom class name */
|
|
7
|
+
class?: string;
|
|
8
|
+
/** Children content */
|
|
9
|
+
children: Snippet;
|
|
10
|
+
}
|
|
11
|
+
declare const VizelProvider: import("svelte").Component<VizelProviderProps, {}, "">;
|
|
12
|
+
type VizelProvider = ReturnType<typeof VizelProvider>;
|
|
13
|
+
export default VizelProvider;
|
|
14
|
+
//# sourceMappingURL=VizelProvider.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VizelProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/components/VizelProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAuBD,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { VizelSaveStatus } from "@vizel/core";
|
|
3
|
+
|
|
4
|
+
export interface VizelSaveIndicatorProps {
|
|
5
|
+
/** Current save status */
|
|
6
|
+
status: VizelSaveStatus;
|
|
7
|
+
/** Timestamp of last successful save */
|
|
8
|
+
lastSaved?: Date | null;
|
|
9
|
+
/** Show relative timestamp (default: true) */
|
|
10
|
+
showTimestamp?: boolean;
|
|
11
|
+
/** Custom class name */
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import { formatVizelRelativeTime } from "@vizel/core";
|
|
18
|
+
import VizelIcon from "./VizelIcon.svelte";
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
status,
|
|
22
|
+
lastSaved = null,
|
|
23
|
+
showTimestamp = true,
|
|
24
|
+
class: className,
|
|
25
|
+
}: VizelSaveIndicatorProps = $props();
|
|
26
|
+
|
|
27
|
+
let relativeTime = $state("");
|
|
28
|
+
|
|
29
|
+
function updateTime() {
|
|
30
|
+
if (lastSaved) {
|
|
31
|
+
relativeTime = formatVizelRelativeTime(lastSaved);
|
|
32
|
+
} else {
|
|
33
|
+
relativeTime = "";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$effect(() => {
|
|
38
|
+
updateTime();
|
|
39
|
+
const intervalId = setInterval(updateTime, 10000);
|
|
40
|
+
|
|
41
|
+
return () => {
|
|
42
|
+
clearInterval(intervalId);
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Watch lastSaved changes
|
|
47
|
+
$effect(() => {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
49
|
+
lastSaved;
|
|
50
|
+
updateTime();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const statusText = $derived.by(() => {
|
|
54
|
+
switch (status) {
|
|
55
|
+
case "saved":
|
|
56
|
+
return "Saved";
|
|
57
|
+
case "saving":
|
|
58
|
+
return "Saving...";
|
|
59
|
+
case "unsaved":
|
|
60
|
+
return "Unsaved";
|
|
61
|
+
case "error":
|
|
62
|
+
return "Error saving";
|
|
63
|
+
default:
|
|
64
|
+
return "";
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const shouldShowTimestamp = $derived(showTimestamp && lastSaved && relativeTime && status === "saved");
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<div
|
|
72
|
+
class="vizel-save-indicator vizel-save-indicator--{status} {className ?? ''}"
|
|
73
|
+
role="status"
|
|
74
|
+
aria-live="polite"
|
|
75
|
+
data-vizel-save-indicator
|
|
76
|
+
>
|
|
77
|
+
<span class="vizel-save-indicator-icon" aria-hidden="true">
|
|
78
|
+
{#if status === "saved"}
|
|
79
|
+
<VizelIcon name="check" />
|
|
80
|
+
{:else if status === "saving"}
|
|
81
|
+
<span class="vizel-save-indicator-spinner" aria-hidden="true">
|
|
82
|
+
<VizelIcon name="loader" />
|
|
83
|
+
</span>
|
|
84
|
+
{:else if status === "unsaved"}
|
|
85
|
+
<VizelIcon name="circle" />
|
|
86
|
+
{:else if status === "error"}
|
|
87
|
+
<VizelIcon name="warning" />
|
|
88
|
+
{/if}
|
|
89
|
+
</span>
|
|
90
|
+
<span class="vizel-save-indicator-text">{statusText}</span>
|
|
91
|
+
{#if shouldShowTimestamp}
|
|
92
|
+
<span class="vizel-save-indicator-timestamp">{relativeTime}</span>
|
|
93
|
+
{/if}
|
|
94
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { VizelSaveStatus } from "@vizel/core";
|
|
2
|
+
export interface VizelSaveIndicatorProps {
|
|
3
|
+
/** Current save status */
|
|
4
|
+
status: VizelSaveStatus;
|
|
5
|
+
/** Timestamp of last successful save */
|
|
6
|
+
lastSaved?: Date | null;
|
|
7
|
+
/** Show relative timestamp (default: true) */
|
|
8
|
+
showTimestamp?: boolean;
|
|
9
|
+
/** Custom class name */
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const VizelSaveIndicator: import("svelte").Component<VizelSaveIndicatorProps, {}, "">;
|
|
13
|
+
type VizelSaveIndicator = ReturnType<typeof VizelSaveIndicator>;
|
|
14
|
+
export default VizelSaveIndicator;
|
|
15
|
+
//# sourceMappingURL=VizelSaveIndicator.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VizelSaveIndicator.svelte.d.ts","sourceRoot":"","sources":["../../src/components/VizelSaveIndicator.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,uBAAuB;IACtC,0BAA0B;IAC1B,MAAM,EAAE,eAAe,CAAC;IACxB,wCAAwC;IACxC,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqFD,QAAA,MAAM,kBAAkB,6DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { VizelSlashCommandItem } from "@vizel/core";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
|
|
5
|
+
export interface VizelSlashMenuRef {
|
|
6
|
+
onKeyDown: (event: KeyboardEvent) => boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface VizelSlashMenuProps {
|
|
10
|
+
/** The list of slash command items */
|
|
11
|
+
items: VizelSlashCommandItem[];
|
|
12
|
+
/** Custom class name */
|
|
13
|
+
class?: string;
|
|
14
|
+
/** Command handler */
|
|
15
|
+
oncommand?: (item: VizelSlashCommandItem) => void;
|
|
16
|
+
/** Whether to show items grouped by category (default: true when not searching) */
|
|
17
|
+
showGroups?: boolean;
|
|
18
|
+
/** Custom group order */
|
|
19
|
+
groupOrder?: string[];
|
|
20
|
+
/** Custom item renderer */
|
|
21
|
+
renderItem?: Snippet<[{ item: VizelSlashCommandItem; isSelected: boolean; onclick: () => void }]>;
|
|
22
|
+
/** Custom empty state renderer */
|
|
23
|
+
renderEmpty?: Snippet;
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<script lang="ts">
|
|
28
|
+
import { groupVizelSlashCommands, type VizelSlashCommandGroup } from "@vizel/core";
|
|
29
|
+
import { tick } from "svelte";
|
|
30
|
+
import VizelSlashMenuItem from "./VizelSlashMenuItem.svelte";
|
|
31
|
+
import VizelSlashMenuEmpty from "./VizelSlashMenuEmpty.svelte";
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
items,
|
|
35
|
+
class: className,
|
|
36
|
+
oncommand,
|
|
37
|
+
showGroups = true,
|
|
38
|
+
groupOrder,
|
|
39
|
+
renderItem,
|
|
40
|
+
renderEmpty,
|
|
41
|
+
}: VizelSlashMenuProps = $props();
|
|
42
|
+
|
|
43
|
+
let selectedIndex = $state(0);
|
|
44
|
+
let itemRefs: (HTMLElement | null)[] = $state([]);
|
|
45
|
+
|
|
46
|
+
// Clean up itemRefs when items decrease
|
|
47
|
+
$effect(() => {
|
|
48
|
+
const length = flatItems.length;
|
|
49
|
+
if (itemRefs.length > length) {
|
|
50
|
+
itemRefs.length = length;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Group items when showGroups is true and there are enough items
|
|
55
|
+
const groups = $derived.by<VizelSlashCommandGroup[]>(() => {
|
|
56
|
+
if (!showGroups || items.length <= 5) {
|
|
57
|
+
// Don't group if explicitly disabled or few items (likely search results)
|
|
58
|
+
return [{ name: "", items }];
|
|
59
|
+
}
|
|
60
|
+
return groupVizelSlashCommands(items, groupOrder);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Flatten for navigation
|
|
64
|
+
const flatItems = $derived(groups.flatMap((g) => g.items));
|
|
65
|
+
|
|
66
|
+
// Reset selection when items change
|
|
67
|
+
$effect(() => {
|
|
68
|
+
items;
|
|
69
|
+
selectedIndex = 0;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Scroll selected item into view when selection changes
|
|
73
|
+
$effect(() => {
|
|
74
|
+
const index = selectedIndex;
|
|
75
|
+
tick().then(() => {
|
|
76
|
+
const selectedElement = itemRefs[index];
|
|
77
|
+
if (selectedElement) {
|
|
78
|
+
selectedElement.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
function selectItem(index: number) {
|
|
84
|
+
const item = flatItems[index];
|
|
85
|
+
if (item) {
|
|
86
|
+
oncommand?.(item);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Navigate to next group with Tab
|
|
91
|
+
function tabHandler() {
|
|
92
|
+
if (groups.length <= 1) return;
|
|
93
|
+
|
|
94
|
+
let currentGroupIndex = 0;
|
|
95
|
+
let itemCount = 0;
|
|
96
|
+
for (let i = 0; i < groups.length; i++) {
|
|
97
|
+
const group = groups[i];
|
|
98
|
+
if (!group) continue;
|
|
99
|
+
if (selectedIndex < itemCount + group.items.length) {
|
|
100
|
+
currentGroupIndex = i;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
itemCount += group.items.length;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Move to next group
|
|
107
|
+
const nextGroupIndex = (currentGroupIndex + 1) % groups.length;
|
|
108
|
+
let nextIndex = 0;
|
|
109
|
+
for (let i = 0; i < nextGroupIndex; i++) {
|
|
110
|
+
const group = groups[i];
|
|
111
|
+
if (group) {
|
|
112
|
+
nextIndex += group.items.length;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
selectedIndex = nextIndex;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Calculate global index for items
|
|
119
|
+
function getGlobalIndex(groupIndex: number, itemIndex: number): number {
|
|
120
|
+
let index = 0;
|
|
121
|
+
for (let i = 0; i < groupIndex; i++) {
|
|
122
|
+
const group = groups[i];
|
|
123
|
+
if (group) {
|
|
124
|
+
index += group.items.length;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return index + itemIndex;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function onKeyDown(event: KeyboardEvent): boolean {
|
|
131
|
+
if (event.key === "ArrowUp") {
|
|
132
|
+
selectedIndex = (selectedIndex + flatItems.length - 1) % flatItems.length;
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (event.key === "ArrowDown") {
|
|
137
|
+
selectedIndex = (selectedIndex + 1) % flatItems.length;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (event.key === "Enter") {
|
|
142
|
+
selectItem(selectedIndex);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (event.key === "Tab") {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
tabHandler();
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<div class="vizel-slash-menu {className ?? ''}" data-vizel-slash-menu>
|
|
157
|
+
{#if flatItems.length === 0}
|
|
158
|
+
{#if renderEmpty}
|
|
159
|
+
{@render renderEmpty()}
|
|
160
|
+
{:else}
|
|
161
|
+
<VizelSlashMenuEmpty />
|
|
162
|
+
{/if}
|
|
163
|
+
{:else}
|
|
164
|
+
{#each groups as group, groupIndex (group.name || groupIndex)}
|
|
165
|
+
{#if group.name}
|
|
166
|
+
<!-- Group with header -->
|
|
167
|
+
<div class="vizel-slash-menu-group" data-vizel-slash-menu-group>
|
|
168
|
+
<div class="vizel-slash-menu-group-header">{group.name}</div>
|
|
169
|
+
{#each group.items as item, itemIndex (item.title)}
|
|
170
|
+
{@const globalIdx = getGlobalIndex(groupIndex, itemIndex)}
|
|
171
|
+
<div bind:this={itemRefs[globalIdx]}>
|
|
172
|
+
{#if renderItem}
|
|
173
|
+
{@render renderItem({
|
|
174
|
+
item,
|
|
175
|
+
isSelected: globalIdx === selectedIndex,
|
|
176
|
+
onclick: () => selectItem(globalIdx),
|
|
177
|
+
})}
|
|
178
|
+
{:else}
|
|
179
|
+
<VizelSlashMenuItem
|
|
180
|
+
{item}
|
|
181
|
+
isSelected={globalIdx === selectedIndex}
|
|
182
|
+
onclick={() => selectItem(globalIdx)}
|
|
183
|
+
/>
|
|
184
|
+
{/if}
|
|
185
|
+
</div>
|
|
186
|
+
{/each}
|
|
187
|
+
</div>
|
|
188
|
+
{:else}
|
|
189
|
+
<!-- Items without group header -->
|
|
190
|
+
{#each group.items as item, itemIndex (item.title)}
|
|
191
|
+
{@const globalIdx = getGlobalIndex(groupIndex, itemIndex)}
|
|
192
|
+
<div bind:this={itemRefs[globalIdx]}>
|
|
193
|
+
{#if renderItem}
|
|
194
|
+
{@render renderItem({
|
|
195
|
+
item,
|
|
196
|
+
isSelected: globalIdx === selectedIndex,
|
|
197
|
+
onclick: () => selectItem(globalIdx),
|
|
198
|
+
})}
|
|
199
|
+
{:else}
|
|
200
|
+
<VizelSlashMenuItem
|
|
201
|
+
{item}
|
|
202
|
+
isSelected={globalIdx === selectedIndex}
|
|
203
|
+
onclick={() => selectItem(globalIdx)}
|
|
204
|
+
/>
|
|
205
|
+
{/if}
|
|
206
|
+
</div>
|
|
207
|
+
{/each}
|
|
208
|
+
{/if}
|
|
209
|
+
{/each}
|
|
210
|
+
{/if}
|
|
211
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { VizelSlashCommandItem } from "@vizel/core";
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
export interface VizelSlashMenuRef {
|
|
4
|
+
onKeyDown: (event: KeyboardEvent) => boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface VizelSlashMenuProps {
|
|
7
|
+
/** The list of slash command items */
|
|
8
|
+
items: VizelSlashCommandItem[];
|
|
9
|
+
/** Custom class name */
|
|
10
|
+
class?: string;
|
|
11
|
+
/** Command handler */
|
|
12
|
+
oncommand?: (item: VizelSlashCommandItem) => void;
|
|
13
|
+
/** Whether to show items grouped by category (default: true when not searching) */
|
|
14
|
+
showGroups?: boolean;
|
|
15
|
+
/** Custom group order */
|
|
16
|
+
groupOrder?: string[];
|
|
17
|
+
/** Custom item renderer */
|
|
18
|
+
renderItem?: Snippet<[{
|
|
19
|
+
item: VizelSlashCommandItem;
|
|
20
|
+
isSelected: boolean;
|
|
21
|
+
onclick: () => void;
|
|
22
|
+
}]>;
|
|
23
|
+
/** Custom empty state renderer */
|
|
24
|
+
renderEmpty?: Snippet;
|
|
25
|
+
}
|
|
26
|
+
declare const VizelSlashMenu: import("svelte").Component<VizelSlashMenuProps, {
|
|
27
|
+
onKeyDown: (event: KeyboardEvent) => boolean;
|
|
28
|
+
}, "">;
|
|
29
|
+
type VizelSlashMenu = ReturnType<typeof VizelSlashMenu>;
|
|
30
|
+
export default VizelSlashMenu;
|
|
31
|
+
//# sourceMappingURL=VizelSlashMenu.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VizelSlashMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/components/VizelSlashMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,KAAK,EAAE,qBAAqB,EAAE,CAAC;IAC/B,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yBAAyB;IACzB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,IAAI,EAAE,qBAAqB,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;IAClG,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA8LD,QAAA,MAAM,cAAc;uBA/EO,aAAa,KAAG,OAAO;MA+EU,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|