@threadlabs/looma 0.23.2 → 0.23.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.23.2",
3
+ "version": "0.23.4",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -2,6 +2,7 @@
2
2
  export interface UiTreeEventMap {
3
3
  reorder: CustomEvent<{ readonly sourceId: string; readonly targetId: string; readonly position: "before" | "inside" | "after"; readonly sourceType: string; readonly targetType: string; readonly sourceScope: string; readonly targetScope: string; readonly trigger: "keyboard" | "pointer" | "programmatic" }>;
4
4
  "reorder-rejected": CustomEvent<{ readonly sourceId: string; readonly targetId: string; readonly position: "before" | "inside" | "after"; readonly reason: "max-depth"; readonly maxDepth: number; readonly resultingDepth: number; readonly trigger: "keyboard" | "pointer" | "programmatic" }>;
5
+ activate: CustomEvent<{ readonly id: string; readonly trigger: "keyboard" | "pointer" }>;
5
6
  select: CustomEvent<{ readonly ids: readonly (string)[]; readonly trigger: "keyboard" | "pointer" | "programmatic" }>;
6
7
  }
7
8
  export interface UiTreeElement extends HTMLDivElement {
package/vanilla/UiTree.js CHANGED
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import * as controller from "../components/ui-tree/ui-tree.js";
4
4
  import "../styles/ui-tree.css";
5
5
 
6
- const definition = {...{"contract":{"tag":"ui-tree","props":{"density":{"type":{"kind":"terminal","name":"keyword"},"values":["comfortable","compact"],"required":false,"target":{"attribute":"density"},"default":"comfortable"},"hoverExpandDelay":{"type":"number","required":false,"target":{"attribute":"hoverexpanddelay"},"default":700},"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":"Tree"},"marquee":{"type":"boolean","required":false,"target":{"attribute":"marquee"},"default":false},"maxDepth":{"type":"number","required":false,"target":{"attribute":"maxdepth"},"default":0},"selection":{"type":{"kind":"terminal","name":"keyword"},"values":["none","single","multiple"],"required":false,"target":{"attribute":"selection"},"default":"none"}}},"template":{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"role","value":"tree"}],"children":[{"kind":"slot"}]},"declarations":[{"kind":"event","name":"reorder","type":"object","shape":{"kind":"object","fields":[{"name":"sourceId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"position","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["before","inside","after"]},"optional":false},{"name":"sourceType","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetType","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"sourceScope","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetScope","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"trigger","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["keyboard","pointer","programmatic"]},"optional":false}],"open":false},"bubbles":true,"composed":true,"cancelable":false},{"kind":"event","name":"reorder-rejected","type":"object","shape":{"kind":"object","fields":[{"name":"sourceId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"position","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["before","inside","after"]},"optional":false},{"name":"reason","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["max-depth"]},"optional":false},{"name":"maxDepth","type":{"kind":"terminal","name":"integer"},"optional":false},{"name":"resultingDepth","type":{"kind":"terminal","name":"integer"},"optional":false},{"name":"trigger","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["keyboard","pointer","programmatic"]},"optional":false}],"open":false},"bubbles":true,"composed":true,"cancelable":false},{"kind":"event","name":"select","type":"object","shape":{"kind":"object","fields":[{"name":"ids","type":{"kind":"list","item":{"kind":"terminal","name":"string"}},"optional":false},{"name":"trigger","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["keyboard","pointer","programmatic"]},"optional":false}],"open":false},"bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
6
+ const definition = {...{"contract":{"tag":"ui-tree","props":{"density":{"type":{"kind":"terminal","name":"keyword"},"values":["comfortable","compact"],"required":false,"target":{"attribute":"density"},"default":"comfortable"},"hoverExpandDelay":{"type":"number","required":false,"target":{"attribute":"hoverexpanddelay"},"default":700},"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":"Tree"},"marquee":{"type":"boolean","required":false,"target":{"attribute":"marquee"},"default":false},"maxDepth":{"type":"number","required":false,"target":{"attribute":"maxdepth"},"default":0},"selection":{"type":{"kind":"terminal","name":"keyword"},"values":["none","single","multiple"],"required":false,"target":{"attribute":"selection"},"default":"none"}}},"template":{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"role","value":"tree"}],"children":[{"kind":"slot"}]},"declarations":[{"kind":"event","name":"reorder","type":"object","shape":{"kind":"object","fields":[{"name":"sourceId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"position","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["before","inside","after"]},"optional":false},{"name":"sourceType","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetType","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"sourceScope","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetScope","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"trigger","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["keyboard","pointer","programmatic"]},"optional":false}],"open":false},"bubbles":true,"composed":true,"cancelable":false},{"kind":"event","name":"reorder-rejected","type":"object","shape":{"kind":"object","fields":[{"name":"sourceId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"targetId","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"position","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["before","inside","after"]},"optional":false},{"name":"reason","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["max-depth"]},"optional":false},{"name":"maxDepth","type":{"kind":"terminal","name":"integer"},"optional":false},{"name":"resultingDepth","type":{"kind":"terminal","name":"integer"},"optional":false},{"name":"trigger","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["keyboard","pointer","programmatic"]},"optional":false}],"open":false},"bubbles":true,"composed":true,"cancelable":false},{"kind":"event","name":"activate","type":"object","shape":{"kind":"object","fields":[{"name":"id","type":{"kind":"terminal","name":"string"},"optional":false},{"name":"trigger","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["keyboard","pointer"]},"optional":false}],"open":false},"bubbles":true,"composed":true,"cancelable":false},{"kind":"event","name":"select","type":"object","shape":{"kind":"object","fields":[{"name":"ids","type":{"kind":"list","item":{"kind":"terminal","name":"string"}},"optional":false},{"name":"trigger","type":{"kind":"constrained","base":{"kind":"terminal","name":"keyword"},"values":["keyboard","pointer","programmatic"]},"optional":false}],"open":false},"bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
7
7
 
8
8
  export function createUiTree(options = {}) {
9
9
  const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
package/vue/UiTree.d.ts CHANGED
@@ -33,6 +33,10 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
33
33
  readonly ids: readonly (string)[];
34
34
  readonly trigger: "keyboard" | "pointer" | "programmatic";
35
35
  }>) => any;
36
+ activate: (event: CustomEvent<{
37
+ readonly id: string;
38
+ readonly trigger: "keyboard" | "pointer";
39
+ }>) => any;
36
40
  "update:maxDepth": (value: number) => any;
37
41
  reorder: (event: CustomEvent<{
38
42
  readonly sourceId: string;
@@ -83,6 +87,10 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
83
87
  readonly ids: readonly (string)[];
84
88
  readonly trigger: "keyboard" | "pointer" | "programmatic";
85
89
  }>) => any) | undefined;
90
+ onActivate?: ((event: CustomEvent<{
91
+ readonly id: string;
92
+ readonly trigger: "keyboard" | "pointer";
93
+ }>) => any) | undefined;
86
94
  "onUpdate:maxDepth"?: ((value: number) => any) | undefined;
87
95
  onReorder?: ((event: CustomEvent<{
88
96
  readonly sourceId: string;
package/vue/UiTree.vue CHANGED
@@ -54,7 +54,7 @@ const propValidityContract = {
54
54
  required: false,
55
55
  target: { attribute: 'selection' },
56
56
  description:
57
- 'How selection works. None keeps rows for navigation and expansion; single requests one selected item; multiple shows checkboxes and requests an item with\n its descendants. The application owns each Tree Item\'s selected state and responds to select.',
57
+ 'How selection works. None keeps rows for navigation and expansion; single requests one selected item; multiple shows checkboxes and requests an item with\n its descendants. The application owns each Tree Item\'s selected state and responds to select. Single-selection rows also request activate on a row click\n or Enter, including an already selected row. Space requests selection only; controls and disabled rows do not activate.',
58
58
  values: ['none', 'single', 'multiple'],
59
59
  default: 'none',
60
60
  },
@@ -192,6 +192,7 @@ const emit = defineEmits<{
192
192
  }
193
193
  >,
194
194
  ]
195
+ activate: [event: CustomEvent<{ readonly id: string; readonly trigger: 'keyboard' | 'pointer' }>]
195
196
  select: [
196
197
  event: CustomEvent<
197
198
  {
@@ -270,6 +271,12 @@ const isReorderRejectedDetail = (detail: unknown): boolean =>
270
271
  ['sourceId', 'targetId', 'position', 'reason', 'maxDepth', 'resultingDepth', 'trigger']
271
272
  .includes(key)
272
273
  )
274
+ const isActivateDetail = (detail: unknown): boolean =>
275
+ detail !== null && typeof detail === 'object' && !Array.isArray(detail)
276
+ && typeof (detail as Record<string, unknown>)['id'] === 'string'
277
+ && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
278
+ || (detail as Record<string, unknown>)['trigger'] === 'pointer')
279
+ && Object.keys(detail as object).every((key) => ['id', 'trigger'].includes(key))
273
280
  const isSelectDetail = (detail: unknown): boolean =>
274
281
  detail !== null && typeof detail === 'object' && !Array.isArray(detail)
275
282
  && (Array.isArray((detail as Record<string, unknown>)['ids'])
@@ -284,6 +291,7 @@ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) =>
284
291
  checks: {
285
292
  reorder: isReorderDetail,
286
293
  'reorder-rejected': isReorderRejectedDetail,
294
+ activate: isActivateDetail,
287
295
  select: isSelectDetail,
288
296
  },
289
297
  modeled: ['maxDepth'],
@@ -47,6 +47,7 @@ var UiTree_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
47
47
  "update:maxDepth",
48
48
  "reorder",
49
49
  "reorder-rejected",
50
+ "activate",
50
51
  "select"
51
52
  ],
52
53
  setup(__props, { emit: __emit }) {
@@ -109,7 +110,7 @@ var UiTree_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
109
110
  },
110
111
  required: false,
111
112
  target: { attribute: "selection" },
112
- description: "How selection works. None keeps rows for navigation and expansion; single requests one selected item; multiple shows checkboxes and requests an item with\n its descendants. The application owns each Tree Item's selected state and responds to select.",
113
+ description: "How selection works. None keeps rows for navigation and expansion; single requests one selected item; multiple shows checkboxes and requests an item with\n its descendants. The application owns each Tree Item's selected state and responds to select. Single-selection rows also request activate on a row click\n or Enter, including an already selected row. Space requests selection only; controls and disabled rows do not activate.",
113
114
  values: [
114
115
  "none",
115
116
  "single",
@@ -205,11 +206,13 @@ var UiTree_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
205
206
  "resultingDepth",
206
207
  "trigger"
207
208
  ].includes(key));
209
+ const isActivateDetail = (detail) => detail !== null && typeof detail === "object" && !Array.isArray(detail) && typeof detail["id"] === "string" && (detail["trigger"] === "keyboard" || detail["trigger"] === "pointer") && Object.keys(detail).every((key) => ["id", "trigger"].includes(key));
208
210
  const isSelectDetail = (detail) => detail !== null && typeof detail === "object" && !Array.isArray(detail) && Array.isArray(detail["ids"]) && detail["ids"].every((item) => typeof item === "string") && (detail["trigger"] === "keyboard" || detail["trigger"] === "pointer" || detail["trigger"] === "programmatic") && Object.keys(detail).every((key) => ["ids", "trigger"].includes(key));
209
211
  const dispatch = createDispatch(root, emit, {
210
212
  checks: {
211
213
  reorder: isReorderDetail,
212
214
  "reorder-rejected": isReorderRejectedDetail,
215
+ activate: isActivateDetail,
213
216
  select: isSelectDetail
214
217
  },
215
218
  modeled: ["maxDepth"]
@@ -260,6 +263,6 @@ var UiTree_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
260
263
  }]]);
261
264
  };
262
265
  }
263
- }), [["__scopeId", "data-v-ee69a15a"]]);
266
+ }), [["__scopeId", "data-v-ba04ad17"]]);
264
267
  //#endregion
265
268
  export { UiTree_default as t };
@@ -8245,17 +8245,17 @@ to {
8245
8245
 
8246
8246
  /* Reaches the items the way density does, so the setting lives on the tree the way a reader
8247
8247
  thinks about it: this list scrolls its long names. */
8248
- [data-component~="ui-tree"][data-ui-tree-state~="marquee"][data-v-ee69a15a] {
8248
+ [data-component~="ui-tree"][data-ui-tree-state~="marquee"][data-v-ba04ad17] {
8249
8249
  --_ui-default-tree-item-marquee: 1;
8250
8250
  }
8251
8251
 
8252
8252
  /* The items' defaults for this density: an item's own hook still wins. */
8253
- [data-component~="ui-tree"][data-ui-tree-state~="density=compact"][data-v-ee69a15a] {
8253
+ [data-component~="ui-tree"][data-ui-tree-state~="density=compact"][data-v-ba04ad17] {
8254
8254
  --_ui-default-tree-item-min-block-size: var(--ui-control-size-sm);
8255
8255
  --_ui-default-tree-item-font-size: var(--ui-font-size-sm);
8256
8256
  --_ui-default-tree-item-label-padding-block: 0;
8257
8257
  }
8258
- [data-component~="ui-tree"][data-v-ee69a15a] {
8258
+ [data-component~="ui-tree"][data-v-ba04ad17] {
8259
8259
  /* Hooks do not inherit: the tree hands its own to its items here, and a tree nested in an item
8260
8260
  starts again from its own settings. */
8261
8261
  --_ui-tree-indent: var(--ui-tree-indent);
@@ -8268,7 +8268,7 @@ to {
8268
8268
  min-inline-size: 0;
8269
8269
  padding-inline-start: var(--ui-tree-gutter, 0px);
8270
8270
  }
8271
- [data-component~="ui-tree"][data-move-mode][data-v-ee69a15a]::after {
8271
+ [data-component~="ui-tree"][data-move-mode][data-v-ba04ad17]::after {
8272
8272
  content: "Choose a destination row, or use Up and Down; Right nests inside a branch. Enter places it, Escape cancels.";
8273
8273
  display: block;
8274
8274
  padding: var(--ui-space-2);
@@ -8277,7 +8277,7 @@ to {
8277
8277
  }
8278
8278
 
8279
8279
  /* A component that sets its display still honours the hidden attribute on its root. */
8280
- [data-component~="ui-tree"][hidden][data-v-ee69a15a] {
8280
+ [data-component~="ui-tree"][hidden][data-v-ba04ad17] {
8281
8281
  display: none;
8282
8282
  }
8283
8283
 
@@ -1839,7 +1839,8 @@ var LoomaEditor = defineComponent({
1839
1839
  "data-active": instance.isActive("link") ? "true" : "false",
1840
1840
  onPointerenter: () => showTool(linkTriggerId, "Link", false),
1841
1841
  onPointerleave: hideTool,
1842
- onPointerdown: () => {
1842
+ onPointerdown: (event) => {
1843
+ event.preventDefault();
1843
1844
  linkPopoverAnchorId.value = linkTriggerId;
1844
1845
  const selection = instance.state.selection;
1845
1846
  linkPressedSelection = selection.empty ? null : {
@@ -2048,7 +2049,7 @@ var LoomaEditor = defineComponent({
2048
2049
  value: codeUi.language,
2049
2050
  onValueChange: (event) => chooseCodeLanguage(event.detail)
2050
2051
  }, () => codeLanguageOptions.value.map((option) => h("option", { value: option.value }, option.label)))]) : null,
2051
- instance && props.editable && mobile.value && editorFocused.value ? h("div", {
2052
+ instance && props.editable && mobile.value && (editorFocused.value || linkOpen.value) ? h("div", {
2052
2053
  ref: mobileToolbarShell,
2053
2054
  class: "looma-editor__mobile-toolbar-shell",
2054
2055
  "data-mode": mobileToolbarMode.value,