@threadlabs/looma 0.9.2 → 0.10.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.
Files changed (49) hide show
  1. package/components/shared/form-reset.js +21 -0
  2. package/components/ui-checkbox/ui-checkbox.html +17 -4
  3. package/components/ui-checkbox/ui-checkbox.js +9 -0
  4. package/components/ui-combobox/ui-combobox.html +23 -11
  5. package/components/ui-combobox/ui-combobox.js +31 -12
  6. package/components/ui-editable/ui-editable.html +3 -1
  7. package/components/ui-editable/ui-editable.js +7 -6
  8. package/components/ui-editor-toolbar/ui-editor-toolbar.html +70 -20
  9. package/components/ui-editor-toolbar/ui-editor-toolbar.js +24 -0
  10. package/components/ui-input/ui-input.html +2 -2
  11. package/components/ui-input/ui-input.js +5 -2
  12. package/components/ui-radio/ui-radio.html +3 -1
  13. package/components/ui-radio/ui-radio.js +11 -0
  14. package/components/ui-radio-group/ui-radio-group.html +7 -5
  15. package/components/ui-radio-group/ui-radio-group.js +16 -3
  16. package/components/ui-search-result-row/ui-search-result-row.html +3 -3
  17. package/components/ui-select/ui-select.html +3 -1
  18. package/components/ui-select/ui-select.js +5 -2
  19. package/components/ui-switch/ui-switch.html +7 -4
  20. package/components/ui-switch/ui-switch.js +9 -0
  21. package/components/ui-textarea/ui-textarea.html +2 -2
  22. package/components/ui-textarea/ui-textarea.js +5 -2
  23. package/components/ui-tree-item/ui-tree-item.html +2 -2
  24. package/dist/index.js +20 -19
  25. package/package.json +1 -1
  26. package/styles/ui-editor-toolbar.css +72 -18
  27. package/vanilla/UiCheckbox.d.ts +1 -0
  28. package/vanilla/UiCheckbox.js +7 -4
  29. package/vanilla/UiCombobox.js +113 -99
  30. package/vanilla/UiEditorToolbar.js +11 -6
  31. package/vanilla/UiRadioGroup.js +7 -1
  32. package/vanilla/UiSearchResultRow.js +4 -1
  33. package/vanilla/UiSwitch.d.ts +1 -0
  34. package/vanilla/UiSwitch.js +7 -4
  35. package/vue/UiCheckbox.d.ts +2 -0
  36. package/vue/UiCheckbox.vue +3 -0
  37. package/vue/UiCombobox.vue +27 -4
  38. package/vue/UiEditorToolbar.vue +90 -20
  39. package/vue/UiRadioGroup.vue +2 -0
  40. package/vue/UiSearchResultRow.vue +1 -0
  41. package/vue/UiSwitch.d.ts +2 -0
  42. package/vue/UiSwitch.vue +3 -0
  43. package/vue/chunks/UiCheckbox.js +4 -1
  44. package/vue/chunks/UiCombobox.js +67 -39
  45. package/vue/chunks/UiEditorToolbar.js +33 -4
  46. package/vue/chunks/UiRadioGroup.js +8 -2
  47. package/vue/chunks/UiSearchResultRow.js +7 -2
  48. package/vue/chunks/UiSwitch.js +4 -1
  49. package/vue/components.css +159 -115
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import * as controller from "../components/ui-radio-group/ui-radio-group.js";
4
4
  import "../styles/ui-radio-group.css";
5
5
 
6
- const definition = {...{"contract":{"tag":"ui-radio-group","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":"Options"},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"orientation":{"type":{"enum":["horizontal","vertical"]},"required":false,"target":{"attribute":"orientation"},"default":"horizontal"},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":""}}},"template":{"kind":"element","name":"fieldset","attributes":[{"kind":"literal","name":"role","value":"radiogroup"}],"children":[{"kind":"element","name":"legend","attributes":[],"children":[{"kind":"element","name":"template","attributes":[{"kind":"directive","name":"value","expression":"label","expressionPlan":{"source":"label","ast":{"kind":"id","name":"label"},"dependencies":["label"]}}],"children":[]}]},{"kind":"slot"}]},"declarations":[{"kind":"state","name":"internalValue","expression":{"source":"''","ast":{"kind":"literal","value":""},"dependencies":[]}},{"kind":"event","name":"select","type":"object({ value: string, previousValue: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"fieldset","choices":["fieldset"]}},source:{file:import.meta.url},css:""};
6
+ const definition = {...{"contract":{"tag":"ui-radio-group","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":"Options"},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"orientation":{"type":{"enum":["horizontal","vertical"]},"required":false,"target":{"attribute":"orientation"},"default":"horizontal"},"required":{"type":"boolean","required":false,"target":{"attribute":"aria-required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":""}}},"template":{"kind":"element","name":"fieldset","attributes":[{"kind":"literal","name":"role","value":"radiogroup"},{"kind":"attribute","name":"aria-required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}}],"children":[{"kind":"element","name":"legend","attributes":[],"children":[{"kind":"element","name":"template","attributes":[{"kind":"directive","name":"value","expression":"label","expressionPlan":{"source":"label","ast":{"kind":"id","name":"label"},"dependencies":["label"]}}],"children":[]}]},{"kind":"slot"}]},"declarations":[{"kind":"state","name":"internalValue","expression":{"source":"''","ast":{"kind":"literal","value":""},"dependencies":[]}},{"kind":"event","name":"select","type":"object({ value: string, previousValue: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"fieldset","choices":["fieldset"]}},source:{file:import.meta.url},css:""};
7
7
 
8
8
  export function createUiRadioGroup(options = {}) {
9
9
  const { attributes = {}, children = [], slots = {}, as, ...componentProps } = options;
@@ -14,6 +14,12 @@ export function createUiRadioGroup(options = {}) {
14
14
  element.setAttribute(name, value === true ? "" : String(value));
15
15
  }
16
16
  if (!element.hasAttribute("role")) element.setAttribute("role", "radiogroup");
17
+ const value0 = componentProps["required"] === undefined ? false : componentProps["required"];
18
+ if (typeof value0 === "boolean") element.setAttribute("aria-required", String(value0));
19
+ else element.removeAttribute("aria-required");
20
+ const value1 = componentProps["disabled"] === undefined ? false : componentProps["disabled"];
21
+ if (value1 === true) element.setAttribute("disabled", "");
22
+ else element.removeAttribute("disabled");
17
23
  element.setAttribute("data-component", "ui-radio-group");
18
24
  const element0 = document.createElement("legend");
19
25
  const element1 = document.createElement("template");
@@ -2,7 +2,7 @@
2
2
  import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import "../styles/ui-search-result-row.css";
4
4
 
5
- const definition = {...{"contract":{"tag":"ui-search-result-row","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"selected":{"type":"boolean","required":false,"target":{"attribute":"selected"},"default":false}}},"template":{"kind":"element","name":"button","attributes":[{"kind":"literal","name":"type","value":"button"},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"leading"}],"children":[{"kind":"slot","fallback":[],"name":"leading"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"content"}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"title"}],"children":[{"kind":"slot","fallback":[],"name":"title"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"meta"}],"children":[{"kind":"slot","fallback":[],"name":"meta"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"excerpt"}],"children":[{"kind":"slot","fallback":[],"name":"excerpt"}]}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"trailing"}],"children":[{"kind":"slot","fallback":[],"name":"trailing"}]}]},"declarations":[],"root":{"kind":"native","element":"button","choices":["button"]}},source:{file:import.meta.url},css:""};
5
+ const definition = {...{"contract":{"tag":"ui-search-result-row","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"selected":{"type":"boolean","required":false,"target":{"attribute":"aria-current"},"default":false}}},"template":{"kind":"element","name":"button","attributes":[{"kind":"literal","name":"type","value":"button"},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"aria-current","expression":"selected","expressionPlan":{"source":"selected","ast":{"kind":"id","name":"selected"},"dependencies":["selected"]}}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"leading"}],"children":[{"kind":"slot","fallback":[],"name":"leading"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"content"}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"title"}],"children":[{"kind":"slot","fallback":[],"name":"title"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"meta"}],"children":[{"kind":"slot","fallback":[],"name":"meta"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"excerpt"}],"children":[{"kind":"slot","fallback":[],"name":"excerpt"}]}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"trailing"}],"children":[{"kind":"slot","fallback":[],"name":"trailing"}]}]},"declarations":[],"root":{"kind":"native","element":"button","choices":["button"]}},source:{file:import.meta.url},css:""};
6
6
 
7
7
  export function createUiSearchResultRow(options = {}) {
8
8
  const { attributes = {}, children = [], slots = {}, as, ...componentProps } = options;
@@ -16,6 +16,9 @@ export function createUiSearchResultRow(options = {}) {
16
16
  const value0 = componentProps["disabled"] === undefined ? false : componentProps["disabled"];
17
17
  if (value0 === true) element.setAttribute("disabled", "");
18
18
  else element.removeAttribute("disabled");
19
+ const value1 = componentProps["selected"] === undefined ? false : componentProps["selected"];
20
+ if (typeof value1 === "boolean") element.setAttribute("aria-current", String(value1));
21
+ else element.removeAttribute("aria-current");
19
22
  element.setAttribute("data-component", "ui-search-result-row");
20
23
  const element0 = document.createElement("span");
21
24
  element0.setAttribute("class", "leading");
@@ -9,6 +9,7 @@ export interface UiSwitchElement extends HTMLLabelElement {
9
9
  export interface UiSwitchProps {
10
10
  checked?: boolean | null;
11
11
  disabled?: boolean | null;
12
+ name?: string | null;
12
13
  required?: boolean | null;
13
14
  value?: string | null;
14
15
  attributes?: Readonly<Record<string, string | number | boolean | null | undefined>>;
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import * as controller from "../components/ui-switch/ui-switch.js";
4
4
  import "../styles/ui-switch.css";
5
5
 
6
- const definition = {...{"contract":{"tag":"ui-switch","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"checkbox"},{"kind":"literal","name":"role","value":"switch"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
6
+ const definition = {...{"contract":{"tag":"ui-switch","props":{"checked":{"type":"boolean","required":false,"target":{"attribute":"checked"},"default":false},"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"value":{"type":"string","required":false,"target":{"attribute":"value"},"default":"on"}}},"template":{"kind":"element","name":"label","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"checkbox"},{"kind":"literal","name":"role","value":"switch"},{"kind":"property","key":"checked","name":"checked","expression":"internalChecked","expressionPlan":{"source":"internalChecked","ast":{"kind":"id","name":"internalChecked"},"dependencies":["internalChecked"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}},{"kind":"attribute","name":"value","expression":"value","expressionPlan":{"source":"value","ast":{"kind":"id","name":"value"},"dependencies":["value"]}}],"children":[],"ref":"input"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot"}]}]},"declarations":[{"kind":"state","name":"internalChecked","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"label","choices":["label"]}},source:{file:import.meta.url},css:""};
7
7
 
8
8
  export function createUiSwitch(options = {}) {
9
9
  const { attributes = {}, children = [], slots = {}, as, ...componentProps } = options;
@@ -23,9 +23,12 @@ export function createUiSwitch(options = {}) {
23
23
  const value1 = componentProps["required"] === undefined ? false : componentProps["required"];
24
24
  if (value1 === true) element0.setAttribute("required", "");
25
25
  else element0.removeAttribute("required");
26
- const value2 = componentProps["value"] === undefined ? "on" : componentProps["value"];
27
- if (value2 === null || value2 === undefined) element0.removeAttribute("value");
28
- else element0.setAttribute("value", String(value2));
26
+ const value2 = componentProps["name"] === undefined ? "" : componentProps["name"];
27
+ if (value2 === null || value2 === undefined) element0.removeAttribute("name");
28
+ else element0.setAttribute("name", String(value2));
29
+ const value3 = componentProps["value"] === undefined ? "on" : componentProps["value"];
30
+ if (value3 === null || value3 === undefined) element0.removeAttribute("value");
31
+ else element0.setAttribute("value", String(value3));
29
32
  element.append(element0);
30
33
  const element1 = document.createElement("span");
31
34
  element1.setAttribute("class", "label");
@@ -2,6 +2,7 @@ type __VLS_Props = {
2
2
  checked?: boolean;
3
3
  disabled?: boolean;
4
4
  indeterminate?: boolean;
5
+ name?: string;
5
6
  required?: boolean;
6
7
  value?: string;
7
8
  };
@@ -26,6 +27,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
26
27
  "onUpdate:checked"?: ((value: boolean) => any) | undefined;
27
28
  "onUpdate:value"?: ((value: string) => any) | undefined;
28
29
  }>, {
30
+ name: string;
29
31
  value: string;
30
32
  disabled: boolean;
31
33
  checked: boolean;
@@ -11,6 +11,7 @@ const props = withDefaults(
11
11
  checked?: boolean
12
12
  disabled?: boolean
13
13
  indeterminate?: boolean
14
+ name?: string
14
15
  required?: boolean
15
16
  value?: string
16
17
  }>(),
@@ -18,6 +19,7 @@ const props = withDefaults(
18
19
  checked: false,
19
20
  disabled: false,
20
21
  indeterminate: false,
22
+ name: '',
21
23
  required: false,
22
24
  value: 'on',
23
25
  },
@@ -81,6 +83,7 @@ useComponentHost(controllerModule.default, {
81
83
  :indeterminate.prop="indeterminate"
82
84
  :disabled="disabled"
83
85
  :required="required"
86
+ :name="name"
84
87
  :value="value"
85
88
  ref="input"
86
89
  >
@@ -208,6 +208,7 @@ const validation = ref<
208
208
  }
209
209
  >({ status: 'pristine', touched: false, dirty: false, issues: [] })
210
210
  const uid = ref('')
211
+ const submitted = ref('')
211
212
  const groups = ref<
212
213
  {
213
214
  name: string
@@ -455,6 +456,7 @@ const { host, ready } = useComponentHost(controllerModule.default, {
455
456
  lookupError,
456
457
  validation,
457
458
  uid,
459
+ submitted,
458
460
  groups,
459
461
  creatable,
460
462
  createIndex,
@@ -518,13 +520,33 @@ defineExpose({
518
520
  :aria-busy="validationStatus === 'pending'"
519
521
  :value.prop="display"
520
522
  :placeholder="placeholder"
521
- :name="name"
522
523
  :disabled="disabled"
523
524
  :readonly="readonly"
524
- :required="required"
525
+ :required="required && !(multiple && internalItems[0])"
525
526
  ref="input"
526
527
  >
527
- <button v-if="clearable" type="button" data-combobox-action="clear" aria-label="Clear">
528
+ <input
529
+ v-if="name && !multiple"
530
+ type="hidden"
531
+ :name="name"
532
+ :value="submitted"
533
+ :disabled="disabled"
534
+ >
535
+ <template v-if="name && multiple"><input
536
+ v-for="item in internalItems"
537
+ :key="item.id"
538
+ type="hidden"
539
+ :name="name"
540
+ :value="item.value"
541
+ :disabled="disabled"
542
+ ></template>
543
+ <button
544
+ v-if="clearable"
545
+ type="button"
546
+ data-combobox-action="clear"
547
+ aria-label="Clear"
548
+ :disabled="disabled || readonly"
549
+ >
528
550
  <svg
529
551
  class="affordance-icon"
530
552
  aria-hidden="true"
@@ -544,6 +566,7 @@ defineExpose({
544
566
  tabindex="-1"
545
567
  aria-label="Show suggestions"
546
568
  :aria-controls="`${uid}-listbox`"
569
+ :disabled="disabled || readonly"
547
570
  >
548
571
  <svg
549
572
  class="affordance-icon"
@@ -630,7 +653,7 @@ defineExpose({
630
653
  </div>
631
654
  <slot name="footer" />
632
655
  </div>
633
- <div class="message" :id="`${uid}-validation`" :hidden="!validation.issues.length">
656
+ <div class="message" :id="`${uid}-validation`" :hidden="!(validation.issues[0])">
634
657
  <div v-for="(issue, index) in validation.issues" :key="index">{{ issue.message }}</div>
635
658
  </div>
636
659
  <div class="sr-only" role="status" aria-live="polite" aria-atomic="true">{{ statusText }}</div>
@@ -1,6 +1,8 @@
1
1
  <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import { computed } from 'vue'
3
+ import { computed, ref, useTemplateRef } from 'vue'
4
+ import * as controllerModule from '../components/ui-editor-toolbar/ui-editor-toolbar.js'
5
+ import { createDispatch, useComponentHost } from './host'
4
6
 
5
7
  defineOptions({ inheritAttrs: false })
6
8
 
@@ -13,12 +15,29 @@ const props = withDefaults(
13
15
  },
14
16
  )
15
17
 
18
+ const root = ref<HTMLElement | null>(null)
19
+ const stripElement = useTemplateRef<HTMLElement>('strip')
20
+ const overflowStart = ref(false)
21
+ const overflowEnd = ref(false)
22
+
16
23
  /** The values the styles' :host-state() rules test. */
17
24
  const hostState = computed(() =>
18
25
  [
26
+ overflowStart.value && 'overflowStart',
27
+ overflowEnd.value && 'overflowEnd',
19
28
  props.floating && 'floating',
20
29
  ].filter(Boolean).join(' ')
21
30
  )
31
+
32
+ const dispatch = createDispatch(root)
33
+
34
+ useComponentHost(controllerModule.default, {
35
+ root,
36
+ dispatch,
37
+ props,
38
+ refs: { strip: stripElement },
39
+ state: { overflowStart, overflowEnd },
40
+ })
22
41
  </script>
23
42
 
24
43
  <template>
@@ -28,39 +47,89 @@ const hostState = computed(() =>
28
47
  aria-label="Editor toolbar"
29
48
  v-bind="$attrs"
30
49
  :data-ui-editor-toolbar-state="hostState || undefined"
50
+ ref="root"
31
51
  >
32
- <slot />
52
+ <div class="strip" ref="strip"><slot /></div>
33
53
  </div>
34
54
  </template>
35
55
 
36
56
  <style scoped>
37
57
  [data-component~="ui-editor-toolbar"] {
58
+ /* The colour the edge fades blend into: whatever the toolbar is painted with. */
59
+ --_matte: var(--ui-editor-toolbar-surface, var(--ui-surface));
60
+
38
61
  position: sticky;
39
62
  top: 0;
40
63
  z-index: var(--ui-editor-toolbar-z-index, 10);
41
64
  display: flex;
42
65
  align-items: center;
43
- justify-content: flex-start;
44
- gap: var(--ui-editor-toolbar-gap, 2px);
45
66
  padding: var(--ui-editor-toolbar-padding-block, 6px)
46
67
  var(--ui-editor-toolbar-padding-inline, 12px);
47
68
  min-height: var(--ui-editor-toolbar-min-height, 48px);
48
- /* More controls than the column is wide is normal at a readable measure, so the row wraps.
49
- Scrolling hid the tail behind an invisible scrollbar. */
50
- flex-wrap: wrap;
51
- row-gap: var(--ui-editor-toolbar-row-gap, var(--ui-editor-toolbar-gap, 2px));
52
69
  background: var(--ui-editor-toolbar-surface, var(--ui-surface));
53
70
  border-bottom: 1px solid var(--ui-editor-toolbar-border, var(--ui-border));
54
71
  }
55
72
 
56
- [data-component~="ui-editor-toolbar"]::-webkit-scrollbar {
73
+ /* One row, always. When the controls outgrow the space, the row scrolls sideways rather than
74
+ wrapping into several lines, and a fade at each edge that has more to show says so. */
75
+ .strip {
76
+ display: flex;
77
+ flex: 1 1 auto;
78
+ align-items: center;
79
+ justify-content: flex-start;
80
+ gap: var(--ui-editor-toolbar-gap, 2px);
81
+ min-width: 0;
82
+ overflow-x: auto;
83
+ overflow-y: hidden;
84
+ overscroll-behavior-inline: contain;
85
+ scrollbar-width: none;
86
+ }
87
+
88
+ .strip::-webkit-scrollbar {
57
89
  display: none;
58
90
  }
59
91
 
60
- [data-component~="ui-editor-toolbar"] > :slotted(*) {
92
+ .strip > :slotted(*) {
61
93
  flex: 0 0 auto;
62
94
  }
63
95
 
96
+ /* The fades are mattes laid over the strip in the toolbar's own colour, not a mask, so a floating
97
+ toolbar's border and shadow stay whole. Each appears only while that edge hides controls. */
98
+ [data-component~="ui-editor-toolbar"]::before,
99
+ [data-component~="ui-editor-toolbar"]::after {
100
+ position: absolute;
101
+ z-index: 1;
102
+ inset-block: 0;
103
+ inline-size: var(--ui-editor-toolbar-fade-size, 32px);
104
+ content: "";
105
+ opacity: 0;
106
+ pointer-events: none;
107
+ transition: opacity var(--ui-motion-fast, 120ms) var(--ui-motion-ease, ease);
108
+ }
109
+
110
+ [data-component~="ui-editor-toolbar"]::before {
111
+ inset-inline-start: 0;
112
+ background: linear-gradient(to right, var(--_matte), transparent);
113
+ }
114
+
115
+ [data-component~="ui-editor-toolbar"]::after {
116
+ inset-inline-end: 0;
117
+ background: linear-gradient(to left, var(--_matte), transparent);
118
+ }
119
+
120
+ [data-component~="ui-editor-toolbar"]:dir(rtl)::before {
121
+ background: linear-gradient(to left, var(--_matte), transparent);
122
+ }
123
+
124
+ [data-component~="ui-editor-toolbar"]:dir(rtl)::after {
125
+ background: linear-gradient(to right, var(--_matte), transparent);
126
+ }
127
+
128
+ [data-component~="ui-editor-toolbar"][data-ui-editor-toolbar-state~="overflowStart"]::before,
129
+ [data-component~="ui-editor-toolbar"][data-ui-editor-toolbar-state~="overflowEnd"]::after {
130
+ opacity: 1;
131
+ }
132
+
64
133
  :slotted(.divider) {
65
134
  width: 1px;
66
135
  height: 20px;
@@ -70,14 +139,12 @@ const hostState = computed(() =>
70
139
  }
71
140
 
72
141
  [data-component~="ui-editor-toolbar"][data-ui-editor-toolbar-state~="floating"] {
73
- flex-wrap: nowrap;
74
- position: static;
142
+ position: relative;
75
143
  top: auto;
76
144
  width: max-content;
77
145
  max-width: min(calc(100vw - 24px), 100%);
78
146
  min-height: auto;
79
147
  padding: var(--ui-editor-toolbar-floating-padding, 4px);
80
- overflow-x: visible;
81
148
  background: color-mix(
82
149
  in srgb,
83
150
  var(--ui-editor-toolbar-surface, var(--ui-surface)) 96%,
@@ -106,7 +173,7 @@ const hostState = computed(() =>
106
173
  }
107
174
 
108
175
  [data-component~="ui-editor-toolbar"][data-ui-editor-toolbar-state~="floating"] {
109
- position: static;
176
+ position: relative;
110
177
  inset: auto;
111
178
  min-height: auto;
112
179
  border: 1px solid var(--ui-editor-toolbar-border, var(--ui-border));
@@ -120,9 +187,10 @@ const hostState = computed(() =>
120
187
 
121
188
  /* Inside LoomaEditor's sticky shell, the shell is the sticky surface. */
122
189
  .looma-editor__sticky-toolbar-shell [data-component~="ui-editor-toolbar"] {
123
- position: static;
190
+ --_matte: var(--ui-editor-toolbar-surface, var(--ui-surface));
191
+
192
+ position: relative;
124
193
  min-height: var(--ui-editor-toolbar-min-height, 48px);
125
- overscroll-behavior-inline: contain;
126
194
  background: transparent;
127
195
  border: 0;
128
196
  box-shadow: none;
@@ -135,10 +203,6 @@ const hostState = computed(() =>
135
203
  max-width: none;
136
204
  min-height: 100%;
137
205
  padding: 0;
138
- overflow-x: auto;
139
- overflow-y: hidden;
140
- overscroll-behavior-inline: contain;
141
- scroll-snap-type: x mandatory;
142
206
  background: transparent;
143
207
  border: 0;
144
208
  border-radius: 0;
@@ -146,6 +210,12 @@ const hostState = computed(() =>
146
210
  backdrop-filter: none;
147
211
  }
148
212
 
213
+ .looma-editor__mobile-toolbar-shell
214
+ [data-component~="ui-editor-toolbar"][data-ui-editor-toolbar-state~="floating"]
215
+ .strip {
216
+ scroll-snap-type: x mandatory;
217
+ }
218
+
149
219
  /* A component that sets its display still honours the hidden attribute on its root. */
150
220
  [data-component~="ui-editor-toolbar"][hidden] {
151
221
  display: none;
@@ -91,6 +91,8 @@ useComponentHost(controllerModule.default, {
91
91
  data-component="ui-radio-group"
92
92
  role="radiogroup"
93
93
  v-bind="$attrs"
94
+ :aria-required="required"
95
+ :disabled="disabled"
94
96
  :data-ui-radio-group-state="hostState || undefined"
95
97
  ref="root"
96
98
  >
@@ -29,6 +29,7 @@ const hostState = computed(() =>
29
29
  type="button"
30
30
  v-bind="$attrs"
31
31
  :disabled="disabled"
32
+ :aria-current="selected"
32
33
  :data-ui-search-result-row-state="hostState || undefined"
33
34
  >
34
35
  <span class="leading"><slot name="leading" /></span>
package/vue/UiSwitch.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  type __VLS_Props = {
2
2
  checked?: boolean;
3
3
  disabled?: boolean;
4
+ name?: string;
4
5
  required?: boolean;
5
6
  value?: string;
6
7
  };
@@ -25,6 +26,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
25
26
  "onUpdate:checked"?: ((value: boolean) => any) | undefined;
26
27
  "onUpdate:value"?: ((value: string) => any) | undefined;
27
28
  }>, {
29
+ name: string;
28
30
  value: string;
29
31
  disabled: boolean;
30
32
  checked: boolean;
package/vue/UiSwitch.vue CHANGED
@@ -10,12 +10,14 @@ const props = withDefaults(
10
10
  defineProps<{
11
11
  checked?: boolean
12
12
  disabled?: boolean
13
+ name?: string
13
14
  required?: boolean
14
15
  value?: string
15
16
  }>(),
16
17
  {
17
18
  checked: false,
18
19
  disabled: false,
20
+ name: '',
19
21
  required: false,
20
22
  value: 'on',
21
23
  },
@@ -79,6 +81,7 @@ useComponentHost(controllerModule.default, {
79
81
  :checked.prop="internalChecked"
80
82
  :disabled="disabled"
81
83
  :required="required"
84
+ :name="name"
82
85
  :value="value"
83
86
  ref="input"
84
87
  >
@@ -9,6 +9,7 @@ var _hoisted_2 = [
9
9
  ".indeterminate",
10
10
  "disabled",
11
11
  "required",
12
+ "name",
12
13
  "value"
13
14
  ];
14
15
  var _hoisted_3 = { class: "label" };
@@ -30,6 +31,7 @@ var UiCheckbox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
30
31
  type: Boolean,
31
32
  default: false
32
33
  },
34
+ name: { default: "" },
33
35
  required: {
34
36
  type: Boolean,
35
37
  default: false
@@ -76,11 +78,12 @@ var UiCheckbox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
76
78
  ".indeterminate": __props.indeterminate,
77
79
  disabled: __props.disabled,
78
80
  required: __props.required,
81
+ name: __props.name,
79
82
  value: __props.value,
80
83
  ref: "input"
81
84
  }, null, 40, _hoisted_2), createElementVNode("span", _hoisted_3, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])], 16, _hoisted_1);
82
85
  };
83
86
  }
84
- }), [["__scopeId", "data-v-964081a0"]]);
87
+ }), [["__scopeId", "data-v-44d93832"]]);
85
88
  //#endregion
86
89
  export { UiCheckbox_default as t };