@threadlabs/looma 0.1.5 → 0.1.6

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/tokens.css CHANGED
@@ -102,6 +102,23 @@
102
102
  --ui-warning-soft: #fdf7e6;
103
103
  --ui-info-solid: #0066cc;
104
104
  --ui-info-soft: #e8f0fb;
105
+
106
+ /* Compatibility aliases used by the editor's public styling surface.
107
+ Each alias resolves through the canonical semantic token so an app only
108
+ needs to override the canonical Looma theme contract. */
109
+ --ui-surface: var(--ui-surface-elevated);
110
+ --ui-surface-hover: var(--ui-surface-muted);
111
+ --ui-text: var(--ui-text-primary);
112
+ --ui-border: var(--ui-border-default);
113
+ --ui-accent: var(--ui-accent-solid);
114
+ --ui-accent-subtle: var(--ui-accent-soft);
115
+ --ui-accent-contrast: var(--ui-text-on-accent);
116
+ --ui-danger: var(--ui-danger-solid);
117
+ --ui-radius-sm: var(--ui-radius-1);
118
+ --ui-radius-md: var(--ui-radius-2);
119
+ --ui-radius-lg: var(--ui-radius-3);
120
+ --ui-radius-xl: var(--ui-radius-4);
121
+ --ui-text-sm: var(--ui-font-size-sm);
105
122
  }
106
123
 
107
124
  @supports (color: oklch(0.5 0.1 240)) {
@@ -0,0 +1,203 @@
1
+ // src/index.ts
2
+ import "@threadlabs/looma/layout";
3
+ import "@threadlabs/looma/core";
4
+
5
+ // src/adapter.ts
6
+ import {
7
+ cloneVNode,
8
+ defineComponent,
9
+ h,
10
+ isVNode,
11
+ shallowRef,
12
+ watchEffect
13
+ } from "vue";
14
+ var BASE_EVENT_BINDINGS = [
15
+ ["open", "onOpen"],
16
+ ["close", "onClose"],
17
+ ["select", "onSelect"],
18
+ ["change", "onChange"],
19
+ ["input", "onInput"],
20
+ ["dismiss", "onDismiss"]
21
+ ];
22
+ function toHTMLElement(value) {
23
+ if (!value) return null;
24
+ if (value instanceof HTMLElement) return value;
25
+ return null;
26
+ }
27
+ function createAdapterComponent(tagName, displayName, additionalEventBindings = []) {
28
+ const eventBindings = [...BASE_EVENT_BINDINGS, ...additionalEventBindings];
29
+ const callbackAttrs = new Set(eventBindings.map(([, callbackAttr]) => callbackAttr));
30
+ return defineComponent({
31
+ name: displayName,
32
+ inheritAttrs: false,
33
+ setup(_props, { attrs, slots }) {
34
+ const elementRef = shallowRef(null);
35
+ watchEffect((onCleanup) => {
36
+ const element = elementRef.value;
37
+ if (!element) return;
38
+ const adapterAttrs = attrs;
39
+ const handlers = eventBindings.map(
40
+ ([eventName, callbackAttr]) => {
41
+ const callback = adapterAttrs[callbackAttr];
42
+ return [
43
+ eventName,
44
+ typeof callback === "function" ? (event) => callback(event.detail) : void 0
45
+ ];
46
+ }
47
+ );
48
+ for (const [eventName, handler] of handlers) {
49
+ if (handler) element.addEventListener(eventName, handler);
50
+ }
51
+ onCleanup(() => {
52
+ for (const [eventName, handler] of handlers) {
53
+ if (handler) element.removeEventListener(eventName, handler);
54
+ }
55
+ });
56
+ });
57
+ return () => {
58
+ const forwardedAttrs = Object.fromEntries(
59
+ Object.entries(attrs).filter(([name]) => !callbackAttrs.has(name))
60
+ );
61
+ const children = Object.entries(slots).flatMap(([slotName, slotFn]) => {
62
+ if (!slotFn) return [];
63
+ return slotFn().map((node) => {
64
+ if (slotName === "default") return node;
65
+ if (isVNode(node)) return cloneVNode(node, { slot: slotName });
66
+ return h("span", { slot: slotName }, node);
67
+ });
68
+ });
69
+ return h(
70
+ tagName,
71
+ {
72
+ "data-allow-mismatch": forwardedAttrs["data-allow-mismatch"] ?? "class",
73
+ ...forwardedAttrs,
74
+ ref: (value) => {
75
+ elementRef.value = toHTMLElement(value);
76
+ }
77
+ },
78
+ children.length > 0 ? children : void 0
79
+ );
80
+ };
81
+ }
82
+ });
83
+ }
84
+
85
+ // src/index.ts
86
+ var Stack = createAdapterComponent("ui-stack", "Stack");
87
+ var Inline = createAdapterComponent("ui-inline", "Inline");
88
+ var Cluster = createAdapterComponent("ui-cluster", "Cluster");
89
+ var Grid = createAdapterComponent("ui-grid", "Grid");
90
+ var Center = createAdapterComponent("ui-center", "Center");
91
+ var Switcher = createAdapterComponent("ui-switcher", "Switcher");
92
+ var Sidebar = createAdapterComponent("ui-sidebar", "Sidebar");
93
+ var Reel = createAdapterComponent("ui-reel", "Reel");
94
+ var Separator = createAdapterComponent("ui-separator", "Separator");
95
+ var Disclosure = createAdapterComponent("ui-disclosure", "Disclosure");
96
+ var Tabs = createAdapterComponent("ui-tabs", "Tabs");
97
+ var Dialog = createAdapterComponent("ui-dialog", "Dialog");
98
+ var Popover = createAdapterComponent("ui-popover", "Popover");
99
+ var Menu = createAdapterComponent("ui-menu", "Menu");
100
+ var MenuItem = createAdapterComponent("ui-menu-item", "MenuItem");
101
+ var ContextMenu = createAdapterComponent("ui-context-menu", "ContextMenu");
102
+ var Button = createAdapterComponent("ui-button", "Button");
103
+ var IconButton = createAdapterComponent("ui-icon-button", "IconButton");
104
+ var Input = createAdapterComponent("ui-input", "Input");
105
+ var Select = createAdapterComponent("ui-select", "Select");
106
+ var Textarea = createAdapterComponent("ui-textarea", "Textarea");
107
+ var FormField = createAdapterComponent("ui-form-field", "FormField");
108
+ var Tooltip = createAdapterComponent("ui-tooltip", "Tooltip");
109
+ var ToastRegion = createAdapterComponent("ui-toast-region", "ToastRegion");
110
+ var Checkbox = createAdapterComponent("ui-checkbox", "Checkbox");
111
+ var Switch = createAdapterComponent("ui-switch", "Switch");
112
+ var RadioGroup = createAdapterComponent("ui-radio-group", "RadioGroup");
113
+ var Radio = createAdapterComponent("ui-radio", "Radio");
114
+ var Badge = createAdapterComponent("ui-badge", "Badge");
115
+ var Avatar = createAdapterComponent("ui-avatar", "Avatar");
116
+ var AvatarGroup = createAdapterComponent("ui-avatar-group", "AvatarGroup");
117
+ var FloatingActionButton = createAdapterComponent(
118
+ "ui-floating-action-button",
119
+ "FloatingActionButton"
120
+ );
121
+ var SearchShell = createAdapterComponent("ui-search-shell", "SearchShell");
122
+ var SearchResultRow = createAdapterComponent("ui-search-result-row", "SearchResultRow");
123
+ var TopBar = createAdapterComponent("ui-top-bar", "TopBar");
124
+ var ADAPTER_COMPONENT_TAG_MAP = {
125
+ Stack: "ui-stack",
126
+ Inline: "ui-inline",
127
+ Cluster: "ui-cluster",
128
+ Grid: "ui-grid",
129
+ Center: "ui-center",
130
+ Switcher: "ui-switcher",
131
+ Sidebar: "ui-sidebar",
132
+ Reel: "ui-reel",
133
+ Separator: "ui-separator",
134
+ Disclosure: "ui-disclosure",
135
+ Tabs: "ui-tabs",
136
+ Dialog: "ui-dialog",
137
+ Popover: "ui-popover",
138
+ Menu: "ui-menu",
139
+ MenuItem: "ui-menu-item",
140
+ ContextMenu: "ui-context-menu",
141
+ Button: "ui-button",
142
+ IconButton: "ui-icon-button",
143
+ Input: "ui-input",
144
+ Select: "ui-select",
145
+ Textarea: "ui-textarea",
146
+ FormField: "ui-form-field",
147
+ Tooltip: "ui-tooltip",
148
+ ToastRegion: "ui-toast-region",
149
+ Checkbox: "ui-checkbox",
150
+ Switch: "ui-switch",
151
+ RadioGroup: "ui-radio-group",
152
+ Radio: "ui-radio",
153
+ Badge: "ui-badge",
154
+ Avatar: "ui-avatar",
155
+ AvatarGroup: "ui-avatar-group",
156
+ FloatingActionButton: "ui-floating-action-button",
157
+ SearchShell: "ui-search-shell",
158
+ SearchResultRow: "ui-search-result-row",
159
+ TopBar: "ui-top-bar"
160
+ };
161
+ var VUE_ADAPTER_NOTE = "Thin adapter only: attrs and slots pass through to custom elements and DOM events map to typed callbacks.";
162
+
163
+ export {
164
+ toHTMLElement,
165
+ createAdapterComponent,
166
+ Stack,
167
+ Inline,
168
+ Cluster,
169
+ Grid,
170
+ Center,
171
+ Switcher,
172
+ Sidebar,
173
+ Reel,
174
+ Separator,
175
+ Disclosure,
176
+ Tabs,
177
+ Dialog,
178
+ Popover,
179
+ Menu,
180
+ MenuItem,
181
+ ContextMenu,
182
+ Button,
183
+ IconButton,
184
+ Input,
185
+ Select,
186
+ Textarea,
187
+ FormField,
188
+ Tooltip,
189
+ ToastRegion,
190
+ Checkbox,
191
+ Switch,
192
+ RadioGroup,
193
+ Radio,
194
+ Badge,
195
+ Avatar,
196
+ AvatarGroup,
197
+ FloatingActionButton,
198
+ SearchShell,
199
+ SearchResultRow,
200
+ TopBar,
201
+ ADAPTER_COMPONENT_TAG_MAP,
202
+ VUE_ADAPTER_NOTE
203
+ };
@@ -1,8 +1,9 @@
1
- import * as vue from 'vue';
2
- import { PropType } from 'vue';
3
1
  import { SlashMenuItem, SlashMenuAnchorRect } from '@threadlabs/looma/editor';
4
2
  export * from '@threadlabs/looma/editor';
3
+ import * as vue from 'vue';
4
+ import { PropType } from 'vue';
5
5
  import { TableContextMenuActionEventDetail, InsertTableEventDetail, TableOverlayActionEventDetail, SlashMenuHighlightEventDetail, SlashMenuSelectEventDetail } from '@threadlabs/looma/editor/ui';
6
+ import { JSONContent, AnyExtension, Editor } from '@tiptap/core';
6
7
 
7
8
  interface VueEditorAdapterEventMap {
8
9
  tableAction: TableContextMenuActionEventDetail;
@@ -86,4 +87,93 @@ declare const EDITOR_ADAPTER_COMPONENT_TAG_MAP: {
86
87
  readonly EditorTableOverlay: "ui-editor-table-overlay";
87
88
  };
88
89
 
89
- export { EDITOR_ADAPTER_COMPONENT_TAG_MAP, EditorInsertTableGrid, EditorSlashMenu, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, type VueEditorAdapterEventMap };
90
+ interface LoomaImageUploadResult {
91
+ url: string;
92
+ alt?: string;
93
+ }
94
+ type LoomaImageUploader = (file: File) => Promise<string | LoomaImageUploadResult>;
95
+ declare const LoomaEditor: vue.DefineComponent<vue.ExtractPropTypes<{
96
+ modelValue: {
97
+ type: PropType<JSONContent>;
98
+ default: () => {
99
+ [key: string]: any;
100
+ type?: string;
101
+ attrs?: Record<string, any>;
102
+ content?: JSONContent[];
103
+ marks?: {
104
+ type: string;
105
+ attrs?: Record<string, any>;
106
+ [key: string]: any;
107
+ }[];
108
+ text?: string;
109
+ };
110
+ };
111
+ editable: {
112
+ type: BooleanConstructor;
113
+ default: boolean;
114
+ };
115
+ placeholder: {
116
+ type: StringConstructor;
117
+ default: string;
118
+ };
119
+ extensions: {
120
+ type: PropType<AnyExtension[]>;
121
+ default: () => never[];
122
+ };
123
+ uploadImage: {
124
+ type: PropType<LoomaImageUploader | undefined>;
125
+ default: undefined;
126
+ };
127
+ }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
128
+ [key: string]: any;
129
+ }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
130
+ "update:modelValue": (_value: JSONContent) => true;
131
+ update: (_value: JSONContent) => true;
132
+ ready: (_editor: Editor) => true;
133
+ uploadError: (_error: unknown, _file: File) => true;
134
+ }, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
135
+ modelValue: {
136
+ type: PropType<JSONContent>;
137
+ default: () => {
138
+ [key: string]: any;
139
+ type?: string;
140
+ attrs?: Record<string, any>;
141
+ content?: JSONContent[];
142
+ marks?: {
143
+ type: string;
144
+ attrs?: Record<string, any>;
145
+ [key: string]: any;
146
+ }[];
147
+ text?: string;
148
+ };
149
+ };
150
+ editable: {
151
+ type: BooleanConstructor;
152
+ default: boolean;
153
+ };
154
+ placeholder: {
155
+ type: StringConstructor;
156
+ default: string;
157
+ };
158
+ extensions: {
159
+ type: PropType<AnyExtension[]>;
160
+ default: () => never[];
161
+ };
162
+ uploadImage: {
163
+ type: PropType<LoomaImageUploader | undefined>;
164
+ default: undefined;
165
+ };
166
+ }>> & Readonly<{
167
+ "onUpdate:modelValue"?: (_value: JSONContent) => any;
168
+ onUpdate?: (_value: JSONContent) => any;
169
+ onReady?: (_editor: Editor) => any;
170
+ onUploadError?: (_error: unknown, _file: File) => any;
171
+ }>, {
172
+ placeholder: string;
173
+ modelValue: JSONContent;
174
+ editable: boolean;
175
+ extensions: AnyExtension[];
176
+ uploadImage: LoomaImageUploader | undefined;
177
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
178
+
179
+ export { EDITOR_ADAPTER_COMPONENT_TAG_MAP, EditorInsertTableGrid, EditorSlashMenu, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, LoomaEditor, type LoomaImageUploadResult, type LoomaImageUploader, type VueEditorAdapterEventMap };