@threadlabs/looma 0.35.1 → 0.36.0

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 (52) hide show
  1. package/components/ui-button/ui-button.html +11 -4
  2. package/components/ui-color-picker/ui-color-picker.html +1 -1
  3. package/components/ui-combobox/ui-combobox.html +1 -1
  4. package/components/ui-curve-editor/ui-curve-editor.html +1 -0
  5. package/components/ui-curve-editor/ui-curve-editor.js +15 -6
  6. package/components/ui-input/ui-input.html +4 -1
  7. package/components/ui-light-editor/ui-light-editor.html +143 -19
  8. package/components/ui-light-editor/ui-light-editor.js +26 -9
  9. package/components/ui-select/ui-select.html +10 -6
  10. package/dist/index.js +1 -1
  11. package/docs/README.md +33 -7
  12. package/docs/card.js +1 -0
  13. package/docs/catalog.json +37 -3
  14. package/docs/customizer-render.mjs +26 -8
  15. package/docs/customizer.js +122 -18
  16. package/docs/fonts.mjs +45 -0
  17. package/docs/index.d.ts +2 -2
  18. package/docs/palette.mjs +67 -0
  19. package/docs/site.mjs +1 -1
  20. package/docs/theme-session.mjs +28 -9
  21. package/docs/theme.mjs +67 -27
  22. package/docs/value-editors.mjs +6 -4
  23. package/package.json +2 -2
  24. package/styles/ui-button.css +11 -4
  25. package/styles/ui-color-picker.css +1 -1
  26. package/styles/ui-combobox.css +2 -2
  27. package/styles/ui-input.css +4 -1
  28. package/styles/ui-light-editor.css +111 -16
  29. package/styles/ui-select.css +10 -6
  30. package/theme-system.css +14 -2
  31. package/tokens.css +10 -0
  32. package/vanilla/UiCurveEditor.d.ts +1 -0
  33. package/vanilla/UiCurveEditor.js +1 -1
  34. package/vanilla/UiLightEditor.d.ts +1 -0
  35. package/vanilla/UiLightEditor.js +2 -2
  36. package/vue/UiButton.vue +4 -2
  37. package/vue/UiColorPicker.vue +1 -1
  38. package/vue/UiCombobox.vue +2 -2
  39. package/vue/UiCurveEditor.d.ts +6 -0
  40. package/vue/UiCurveEditor.vue +2 -0
  41. package/vue/UiInput.vue +4 -1
  42. package/vue/UiLightEditor.d.ts +8 -0
  43. package/vue/UiLightEditor.vue +22 -3
  44. package/vue/UiSelect.vue +1 -1
  45. package/vue/chunks/UiButton.js +1 -1
  46. package/vue/chunks/UiColorPicker.js +1 -1
  47. package/vue/chunks/UiCombobox.js +1 -1
  48. package/vue/chunks/UiCurveEditor.js +5 -3
  49. package/vue/chunks/UiInput.js +1 -1
  50. package/vue/chunks/UiLightEditor.js +16 -6
  51. package/vue/chunks/UiSelect.js +1 -1
  52. package/vue/components.css +41 -14
@@ -7,22 +7,117 @@
7
7
  [data-component~="ui-light-editor"] { --html-next-scope-owner: "ui-light-editor" !important; }
8
8
  }
9
9
  @scope ([data-component~="ui-light-editor"]) to ([data-component], [data-slotted]) {
10
- :scope:where([data-component~="ui-light-editor"]) { display: grid !important; gap: var(--ui-space-3) !important; min-inline-size: 0; padding: 0 !important; margin: 0 !important; }
11
- .stage { box-sizing: border-box !important; display: block !important; block-size: 13rem; padding: calc(var(--ui-control-min-block-size) / 2) !important; border: var(--ui-border-width) solid var(--ui-control-border); border-radius: var(--ui-field-radius, var(--ui-radius-md)); background: var(--ui-neutral-surface, var(--ui-surface)); }
12
- .plane { position: relative; display: block !important; inline-size: 100%; block-size: 100%; }
13
- svg { inline-size: 100%; block-size: 100%; overflow: visible; }
14
- path { fill: none; stroke: var(--ui-border); stroke-width: 1; vector-effect: non-scaling-stroke; }
15
- .specimen { position: absolute; inset: 0; display: grid !important; place-items: center !important; }
16
- .source { position: absolute; left: 50%; top: 0; translate: -50% -50%; display: grid !important; place-items: center !important; inline-size: max(2rem, var(--ui-control-min-block-size)); block-size: max(2rem, var(--ui-control-min-block-size)); padding: 0 !important; margin: 0 !important; border: 0; border-radius: 50%; background: transparent; cursor: grab; touch-action: none; }
17
- .source:where(:not([data-component], [data-slotted]))::after { content: ""; inline-size: 1rem; block-size: 1rem; border: var(--ui-border-width) solid var(--ui-accent); border-radius: 50%; background: var(--ui-surface-raised); box-shadow: 0 0 0 var(--ui-space-2) color-mix(in srgb, var(--ui-accent) 12%, transparent), var(--ui-shadow-sm); }
18
- .source:active { cursor: grabbing; }
19
- .source:focus-visible { outline: var(--ui-focus-width) solid var(--ui-focus-ring); outline-offset: var(--ui-space-1); }
20
- .source:disabled { cursor: not-allowed; }
21
- .source:disabled:where(:not([data-component], [data-slotted]))::after { background: var(--ui-disabled-surface); border-color: var(--ui-disabled-border); box-shadow: none; }
22
- .coordinates { display: grid !important; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--ui-space-2) !important; }
23
- .hint { font-size: var(--ui-font-size-sm); color: var(--ui-text-secondary); }
24
- @media (forced-colors: active) { .source:where(:not([data-component], [data-slotted]))::after { background: Canvas; border-color: CanvasText; } .source:focus-visible { outline-color: Highlight; } }
10
+ :scope:where([data-component~="ui-light-editor"]) {
11
+ display: grid !important;
12
+ gap: var(--ui-space-3) !important;
13
+ min-inline-size: 0;
14
+ padding: 0 !important;
15
+ margin: 0 !important;
16
+ }
17
+ .stage {
18
+ box-sizing: border-box !important;
19
+ display: block !important;
20
+ block-size: 13rem;
21
+ padding: calc(var(--ui-control-min-block-size) / 2) !important;
22
+ border: var(--ui-border-width) solid var(--ui-control-border);
23
+ border-radius: var(--ui-field-radius, var(--ui-radius-md));
24
+ background: var(--ui-neutral-surface, var(--ui-surface));
25
+ }
26
+ .plane {
27
+ position: relative;
28
+ display: block !important;
29
+ inline-size: 100%;
30
+ block-size: 100%;
31
+ }
32
+ svg {
33
+ inline-size: 100%;
34
+ block-size: 100%;
35
+ overflow: visible;
36
+ }
37
+ path {
38
+ fill: none;
39
+ stroke: var(--ui-border);
40
+ stroke-width: 1;
41
+ vector-effect: non-scaling-stroke;
42
+ }
43
+ .specimen {
44
+ position: absolute;
45
+ inset: 0;
46
+ display: grid !important;
47
+ place-items: center !important;
48
+ }
49
+ .source {
50
+ position: absolute;
51
+ left: 50%;
52
+ top: 0;
53
+ translate: -50% -50%;
54
+ display: grid !important;
55
+ place-items: center !important;
56
+ inline-size: max(2rem, var(--ui-control-min-block-size));
57
+ block-size: max(2rem, var(--ui-control-min-block-size));
58
+ padding: 0 !important;
59
+ margin: 0 !important;
60
+ border: 0;
61
+ border-radius: 50%;
62
+ background: transparent;
63
+ cursor: grab;
64
+ touch-action: none;
65
+ }
66
+ .source-glyph {
67
+ position: relative;
68
+ z-index: 1;
69
+ display: inline-flex !important;
70
+ color: var(--ui-accent);
71
+ font-size: var(--ui-font-size-lg);
72
+ }
73
+ .source:where(:not([data-component], [data-slotted]))::after {
74
+ content: "";
75
+ position: absolute;
76
+ inline-size: 2rem;
77
+ block-size: 2rem;
78
+ border: var(--ui-border-width) solid var(--ui-accent);
79
+ border-radius: 50%;
80
+ background: var(--ui-surface-raised);
81
+ box-shadow: 0 0 0 var(--ui-space-2) color-mix(in srgb, var(--ui-accent) 12%, transparent), var(--ui-shadow-sm);
82
+ }
83
+ .source:active {
84
+ cursor: grabbing;
85
+ }
86
+ .source:focus-visible {
87
+ outline: var(--ui-focus-width) solid var(--ui-focus-ring);
88
+ outline-offset: var(--ui-space-1);
89
+ }
90
+ .source:disabled {
91
+ cursor: not-allowed;
92
+ }
93
+ .source:disabled:where(:not([data-component], [data-slotted]))::after {
94
+ background: var(--ui-disabled-surface);
95
+ border-color: var(--ui-disabled-border);
96
+ box-shadow: none;
97
+ }
98
+ .coordinates {
99
+ display: grid !important;
100
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
101
+ gap: var(--ui-space-2) !important;
102
+ }
103
+ .hint {
104
+ font-size: var(--ui-font-size-sm);
105
+ color: var(--ui-text-secondary);
106
+ }
107
+ @media (forced-colors: active) {
108
+ .source-glyph {
109
+ color: CanvasText;
110
+ }
111
+ .source:where(:not([data-component], [data-slotted]))::after {
112
+ background: Canvas;
113
+ border-color: CanvasText;
114
+ }
115
+ .source:focus-visible {
116
+ outline-color: Highlight;
117
+ }
118
+ }
25
119
  }
26
120
  @scope ([data-component~="ui-light-editor"]) to ([data-component]) {
27
- @media (forced-colors: active) { }
121
+ @media (forced-colors: active) {
122
+ }
28
123
  }
@@ -77,10 +77,12 @@
77
77
  --_ui-select-highlight: var(--ui-surface-highlight, linear-gradient(transparent, transparent));
78
78
  appearance: none;
79
79
  padding-inline-end: calc(var(--ui-space-3) + 1.5rem) !important;
80
- background-image: var(
81
- --ui-select-chevron,
82
- url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c5c58' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
83
- ), var(--_ui-select-highlight);
80
+ background-image:
81
+ var(
82
+ --ui-select-chevron,
83
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c5c58' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
84
+ ),
85
+ var(--_ui-select-highlight);
84
86
  background-repeat: no-repeat;
85
87
  background-position: right var(--ui-space-3) center, center;
86
88
  background-size: 1.125rem 1.125rem, 100% 100%;
@@ -127,7 +129,9 @@
127
129
  padding: var(--ui-control-inset-block-sm, var(--ui-space-1)) var(--ui-space-2) !important;
128
130
  padding-inline-end: calc(var(--ui-space-2) + var(--ui-icon-size-sm) + var(--ui-space-1)) !important;
129
131
  background-position: right var(--ui-space-2) center, center;
130
- background-size: var(--ui-icon-size-sm) var(--ui-icon-size-sm), 100% 100%;
132
+ background-size:
133
+ var(--ui-icon-size-sm) var(--ui-icon-size-sm),
134
+ 100% 100%;
131
135
  font-size: var(--ui-font-size-sm);
132
136
  }
133
137
 
@@ -147,7 +151,7 @@
147
151
  }
148
152
 
149
153
  :scope:where([data-component~="ui-select"])[data-ui-select-state~="size=sm"] {
150
- min-block-size: var(--ui-control-min-block-size);
154
+ min-block-size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
151
155
  font-size: inherit;
152
156
  }
153
157
  }
package/theme-system.css CHANGED
@@ -12,7 +12,8 @@
12
12
 
13
13
  @layer theme {
14
14
  :root {
15
- --ui-density: 1;
15
+ --ui-spacing: 1;
16
+ --ui-control-roominess: 2.5;
16
17
  --ui-scale-ratio: 1.2;
17
18
  --ui-space-curve: 1;
18
19
  --ui-outer-depth: 0.6;
@@ -32,8 +33,9 @@
32
33
  :where(:root, [data-theme], [data-contrast]) {
33
34
  /* Controls use a ratio. Spacing uses an independent curve, so increasing the size
34
35
  contrast cannot unexpectedly turn linear spacing into quadratic spacing. */
36
+ --ui-control-size: calc(var(--ui-font-size) * var(--ui-control-roominess));
35
37
  --_ui-theme-ratio: max(1, var(--ui-scale-ratio));
36
- --_ui-theme-unit: calc(var(--ui-control-size) * 0.1 * max(0, var(--ui-density)));
38
+ --_ui-theme-unit: calc(var(--ui-font-size) * 0.25 * max(0, var(--ui-spacing)));
37
39
  --_ui-theme-curve: max(0, var(--ui-space-curve));
38
40
  --ui-control-size-sm: calc(var(--ui-control-size) / var(--_ui-theme-ratio));
39
41
  --ui-control-size-md: var(--ui-control-size);
@@ -116,6 +118,16 @@
116
118
  --ui-press-scale: 0.98;
117
119
  }
118
120
 
121
+ @media (pointer: coarse) {
122
+ :where(:root, [data-theme], [data-contrast]) {
123
+ --ui-control-size-sm: max(calc(var(--ui-control-size) / var(--_ui-theme-ratio)), var(--ui-control-min-block-size));
124
+ --ui-control-size-md: calc(var(--ui-control-size-sm) * var(--_ui-theme-ratio));
125
+ --ui-control-size-lg: calc(var(--ui-control-size-md) * var(--_ui-theme-ratio));
126
+ /* Small native fields use body-size text on touch to avoid focus zoom. */
127
+ --ui-control-inset-block-sm: max(0px, calc((var(--ui-control-size-sm) - var(--ui-font-size-md) * var(--ui-line-height-tight) - 2 * var(--ui-border-width)) / 2));
128
+ }
129
+ }
130
+
119
131
  @media (prefers-reduced-motion: reduce) {
120
132
  :where(:root, [data-theme], [data-contrast]) {
121
133
  --ui-motion-press: 0ms;
package/tokens.css CHANGED
@@ -283,6 +283,15 @@
283
283
  --ui-motion-fast: 0ms;
284
284
  }
285
285
  }
286
+ /* Raise the family together on touch, so the minimum does not collapse sm and md. */
287
+ @media (pointer: coarse) {
288
+ :root, :where([data-theme], [data-contrast]) {
289
+ --ui-control-size-sm: max(calc(var(--ui-control-size) / 1.2), var(--ui-control-min-block-size));
290
+ --ui-control-size-md: calc(var(--ui-control-size-sm) * 1.2);
291
+ --ui-control-size-lg: calc(var(--ui-control-size-md) * 1.2);
292
+ }
293
+ }
294
+
286
295
  }
287
296
 
288
297
  /* The scroll fade every Looma scroller uses (see ui-scroll-area). Scroll-driven animations grow each
@@ -409,4 +418,5 @@
409
418
  color: var(--ui-disabled-text);
410
419
  cursor: not-allowed;
411
420
  }
421
+
412
422
  }
@@ -2,6 +2,7 @@
2
2
  export interface UiCurveEditorEventMap {
3
3
  input: CustomEvent<{ readonly value: string }>;
4
4
  change: CustomEvent<{ readonly value: string }>;
5
+ cancel: CustomEvent<{ readonly value: string }>;
5
6
  }
6
7
  export interface UiCurveEditorElement extends HTMLSpanElement {
7
8
  addEventListener<K extends keyof UiCurveEditorEventMap>(type: K, listener: (this: UiCurveEditorElement, event: UiCurveEditorEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
@@ -5,7 +5,7 @@ import "../styles/ui-curve-editor.css";
5
5
 
6
6
  const T0 = ["",[],"\n ",["span",["class","plot-frame","role","group"],["span",["class","plot"],"\n ",["svg",["viewBox","0 0 100 100","preserveAspectRatio","none","aria-hidden","true"],["path",["class","guide","d","M0 66.667 H100 M0 33.333 H100"]],["path",["class","guide","d","M0 66.667 L25 63.333"]],["path",["class","guide","d","M100 33.333 L25 33.333"]],["path",["d","M0 66.667 C25 63.333 25 33.333 100 33.333"]]],"\n ",["button",["class","handle","type","button","aria-label","First curve handle. Arrow keys adjust; Shift moves faster."],"\n "],"\n ",["button",["class","handle second","type","button","aria-label","Second curve handle. Arrow keys adjust; Shift moves faster."],"\n "],"\n "]],"\n ",["span",["class","coordinates"],"\n ",["span",["class","coordinate"],["span",[],"X1"],["ui-number-input",["data-curve-coordinate","0","min","0","max","1","step","0.01","label","First handle X"]]],"\n ",["span",["class","coordinate"],["span",[],"Y1"],["ui-number-input",["data-curve-coordinate","1","min","-1","max","2","step","0.01","label","First handle Y"]]],"\n ",["span",["class","coordinate"],["span",[],"X2"],["ui-number-input",["data-curve-coordinate","2","min","0","max","1","step","0.01","label","Second handle X"]]],"\n ",["span",["class","coordinate"],["span",[],"Y2"],["ui-number-input",["data-curve-coordinate","3","min","-1","max","2","step","0.01","label","Second handle Y"]]],"\n "],"\n ",["output",[],0],"\n "];
7
7
  let P0;
8
- const S = { n: ["point0","point1","point2","point3"], t: ["s","s","s","s"], f: import.meta.url, g: "ui-curve-editor", d: { "input": [detailCheck(checkObject([["value", checkString, false]], false)), true, true, false], "change": [detailCheck(checkObject([["value", checkString, false]], false)), true, true, false] }, x: dispatchDeclared, a: (host, I) => C(host, I), b: generatedInputs, i: ["0.25", "0.1", "0.25", "1"] };
8
+ const S = { n: ["point0","point1","point2","point3"], t: ["s","s","s","s"], f: import.meta.url, g: "ui-curve-editor", d: { "input": [detailCheck(checkObject([["value", checkString, false]], false)), true, true, false], "change": [detailCheck(checkObject([["value", checkString, false]], false)), true, true, false], "cancel": [detailCheck(checkObject([["value", checkString, false]], false)), true, true, false] }, x: dispatchDeclared, a: (host, I) => C(host, I), b: generatedInputs, i: ["0.25", "0.1", "0.25", "1"] };
9
9
  const D = { g: "ui-curve-editor", props: { "disabled": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "label": { "required":false,"default":"Value", c: checkString, t: "string", j: 0, m: "s" }, "size": { "required":false,"values":["sm","md","lg"],"default":"md", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "value": { "required":false,"default":"cubic-bezier(0.25, 0.1, 0.25, 1)", c: checkString, t: "string", j: 0, m: "s" } } };
10
10
  const C = (host) => activateModule(controller, host);
11
11
 
@@ -2,6 +2,7 @@
2
2
  export interface UiLightEditorEventMap {
3
3
  input: CustomEvent<{ readonly x: number; readonly y: number }>;
4
4
  change: CustomEvent<{ readonly x: number; readonly y: number }>;
5
+ cancel: CustomEvent<{ readonly x: number; readonly y: number }>;
5
6
  }
6
7
  export interface UiLightEditorElement extends HTMLSpanElement {
7
8
  addEventListener<K extends keyof UiLightEditorEventMap>(type: K, listener: (this: UiLightEditorElement, event: UiLightEditorEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
@@ -3,10 +3,10 @@ import { attachGeneratedController, buildTemplate, NONCONFORMING, textCall, toAt
3
3
  import * as controller from "../components/ui-light-editor/ui-light-editor.js";
4
4
  import "../styles/ui-light-editor.css";
5
5
 
6
- const T0 = ["",[],"\n ",["span",["class","stage","role","group"],"\n ",["span",["class","plane"],"\n ",["svg",["viewBox","0 0 100 60","preserveAspectRatio","none","aria-hidden","true"],["path",["d","M0 30 H100 M50 0 V60"]]],"\n ",["span",["class","specimen"],4],"\n ",["button",["type","button","class","source"]],"\n "],"\n "],"\n ",["span",["class","coordinates"],"\n ",["ui-number-input",["data-coordinate","x","min","0","max","100","step","1","unit","%","label","Light horizontal position"]],"\n ",["ui-number-input",["data-coordinate","y","min","0","max","100","step","1","unit","%","label","Light vertical position"]],"\n "],"\n ",["span",["class","hint"],"Drag the light, use arrow keys, or enter its position. Top centre is 50%, 0%."],"\n "];
6
+ const T0 = ["",[],"\n ",["span",["class","stage","role","group"],"\n ",["span",["class","plane"],"\n ",["svg",["viewBox","0 0 100 60","preserveAspectRatio","none","aria-hidden","true"],["path",["d","M0 30 H100 M50 0 V60"]]],"\n ",["span",["class","specimen"],4],"\n ",["button",["type","button","class","source"],"\n ",["span",["class","source-glyph"],["ui-icon",["name","lightbulb"]]],"\n "],"\n "],"\n "],"\n ",["span",["class","coordinates"],"\n ",["ui-number-input",["data-coordinate","x","min","0","max","100","step","1","unit","%","label","Light horizontal position"]],"\n ",["ui-number-input",["data-coordinate","y","min","0","max","100","step","1","unit","%","label","Light vertical position"]],"\n "],"\n ",["span",["class","hint"],"Drag the light, tap the background, use arrow keys, or enter its position. Escape cancels a drag. Top centre is 50%, 0%."],"\n "];
7
7
  const T1 = ["",[]];
8
8
  let P0, P1;
9
- const S = { n: ["horizontal","vertical"], t: ["s","s"], f: import.meta.url, g: "ui-light-editor", d: { "input": [detailCheck(checkObject([["x", checkNumber, false], ["y", checkNumber, false]], false)), true, true, false], "change": [detailCheck(checkObject([["x", checkNumber, false], ["y", checkNumber, false]], false)), true, true, false] }, x: dispatchDeclared, a: (host, I) => C(host, I), b: generatedInputs, i: ["50", "0"] };
9
+ const S = { n: ["horizontal","vertical"], t: ["s","s"], f: import.meta.url, g: "ui-light-editor", d: { "input": [detailCheck(checkObject([["x", checkNumber, false], ["y", checkNumber, false]], false)), true, true, false], "change": [detailCheck(checkObject([["x", checkNumber, false], ["y", checkNumber, false]], false)), true, true, false], "cancel": [detailCheck(checkObject([["x", checkNumber, false], ["y", checkNumber, false]], false)), true, true, false] }, x: dispatchDeclared, a: (host, I) => C(host, I), b: generatedInputs, i: ["50", "0"] };
10
10
  const D = { g: "ui-light-editor", props: { "disabled": { "required":false,"default":false, c: checkBoolean, t: "boolean", j: 0, m: "b" }, "label": { "required":false,"default":"Light position", c: checkString, t: "string", j: 0, m: "s" }, "size": { "required":false,"values":["sm","md","lg"],"default":"md", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat)] }, "x": { "required":false,"default":0.5, c: checkNumber, t: "number", j: 0, m: "n" }, "y": { "required":false,"default":0, c: checkNumber, t: "number", j: 0, m: "n" } } };
11
11
  const C = (host) => activateModule(controller, host);
12
12
 
package/vue/UiButton.vue CHANGED
@@ -1412,9 +1412,11 @@ useComponentHost(() => import('../components/ui-button/ui-button.js'), {
1412
1412
  border-color: transparent;
1413
1413
  }
1414
1414
  @media (pointer: coarse) {
1415
- :global(:scope:where([data-hn-ui-button])),
1415
+ :global(:scope:where([data-hn-ui-button])) {
1416
+ min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
1417
+ }
1416
1418
  :global(:scope[data-ui-button-state~="size=sm"]:where([data-hn-ui-button])) {
1417
- min-block-size: var(--ui-control-min-block-size);
1419
+ min-block-size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
1418
1420
  }
1419
1421
  }
1420
1422
  :global(html[data-ui-input-modality="touch"]
@@ -414,7 +414,7 @@ useComponentHost(() => import('../components/ui-color-picker/ui-color-picker.js'
414
414
  --_size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
415
415
  }
416
416
  :global(:scope[data-ui-color-picker-state~="size=sm"]) {
417
- --_size: var(--ui-control-min-block-size);
417
+ --_size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
418
418
  --_font: var(--ui-font-size-md);
419
419
  }
420
420
  }
@@ -2388,7 +2388,7 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
2388
2388
  /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
2389
2389
  body-size text: below 16px, iOS zooms the page into the focused field. */
2390
2390
  :global(:scope[data-ui-combobox-state~="size=sm"]:where([data-hn-ui-combobox])) {
2391
- --_row: var(--ui-control-min-block-size);
2391
+ --_row: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
2392
2392
  }
2393
2393
  :global(:scope[data-ui-combobox-state~="size=sm"] input:where([data-hn-ui-combobox])) {
2394
2394
  font-size: inherit;
@@ -2739,7 +2739,7 @@ useComponentHost(() => import('../components/ui-combobox/ui-combobox.js'), {
2739
2739
  /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
2740
2740
  body-size text: below 16px, iOS zooms the page into the focused field. */
2741
2741
  :global(:where(.ui-combobox)[data-ui-combobox-state~="size=sm"]:where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-combobox"]))):where([data-hn-ui-combobox])) {
2742
- --_row: var(--ui-control-min-block-size);
2742
+ --_row: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
2743
2743
  }
2744
2744
  :global(:where(.ui-combobox)[data-ui-combobox-state~="size=sm"]
2745
2745
  input:where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-combobox"]))):where([data-hn-ui-combobox])) {
@@ -20,6 +20,9 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
20
20
  input: (event: CustomEvent<{
21
21
  readonly value: string;
22
22
  }>) => any;
23
+ cancel: (event: CustomEvent<{
24
+ readonly value: string;
25
+ }>) => any;
23
26
  change: (event: CustomEvent<{
24
27
  readonly value: string;
25
28
  }>) => any;
@@ -44,6 +47,9 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
44
47
  onInput?: ((event: CustomEvent<{
45
48
  readonly value: string;
46
49
  }>) => any) | undefined;
50
+ onCancel?: ((event: CustomEvent<{
51
+ readonly value: string;
52
+ }>) => any) | undefined;
47
53
  onChange?: ((event: CustomEvent<{
48
54
  readonly value: string;
49
55
  }>) => any) | undefined;
@@ -136,6 +136,7 @@ const emit = defineEmits<{
136
136
  'update:value': [value: string]
137
137
  input: [event: CustomEvent<{ readonly value: string }>]
138
138
  change: [event: CustomEvent<{ readonly value: string }>]
139
+ cancel: [event: CustomEvent<{ readonly value: string }>]
139
140
  }>()
140
141
 
141
142
  const root = ref<HTMLElement | null>(null)
@@ -165,6 +166,7 @@ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) =>
165
166
  checks: {
166
167
  input: isInputDetail,
167
168
  change: isInputDetail,
169
+ cancel: isInputDetail,
168
170
  },
169
171
  modeled: ['value'],
170
172
  })
package/vue/UiInput.vue CHANGED
@@ -632,7 +632,10 @@ useComponentHost(() => import('../components/ui-input/ui-input.js'), {
632
632
  :global(:scope[data-ui-input-state~="size=sm"]) {
633
633
  min-block-size: var(
634
634
  --ui-input-min-block-size,
635
- var(--_ui-default-input-min-block-size, var(--ui-control-min-block-size))
635
+ var(
636
+ --_ui-default-input-min-block-size,
637
+ max(var(--ui-control-size-sm), var(--ui-control-min-block-size))
638
+ )
636
639
  );
637
640
  }
638
641
  }
@@ -28,6 +28,10 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
28
28
  readonly x: number;
29
29
  readonly y: number;
30
30
  }>) => any;
31
+ cancel: (event: CustomEvent<{
32
+ readonly x: number;
33
+ readonly y: number;
34
+ }>) => any;
31
35
  change: (event: CustomEvent<{
32
36
  readonly x: number;
33
37
  readonly y: number;
@@ -58,6 +62,10 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
58
62
  readonly x: number;
59
63
  readonly y: number;
60
64
  }>) => any) | undefined;
65
+ onCancel?: ((event: CustomEvent<{
66
+ readonly x: number;
67
+ readonly y: number;
68
+ }>) => any) | undefined;
61
69
  onChange?: ((event: CustomEvent<{
62
70
  readonly x: number;
63
71
  readonly y: number;
@@ -23,6 +23,7 @@ import {
23
23
  vPropValidity,
24
24
  vuePropInputs,
25
25
  } from './props'
26
+ import UiIcon from './UiIcon.vue'
26
27
  import UiNumberInput from './UiNumberInput.vue'
27
28
 
28
29
  defineOptions({ inheritAttrs: false })
@@ -148,6 +149,7 @@ const emit = defineEmits<{
148
149
  'update:y': [value: number]
149
150
  input: [event: CustomEvent<{ readonly x: number; readonly y: number }>]
150
151
  change: [event: CustomEvent<{ readonly x: number; readonly y: number }>]
152
+ cancel: [event: CustomEvent<{ readonly x: number; readonly y: number }>]
151
153
  }>()
152
154
 
153
155
  const root = ref<HTMLElement | null>(null)
@@ -180,6 +182,7 @@ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) =>
180
182
  checks: {
181
183
  input: isInputDetail,
182
184
  change: isInputDetail,
185
+ cancel: isInputDetail,
183
186
  },
184
187
  modeled: ['x', 'y'],
185
188
  })
@@ -255,6 +258,10 @@ useComponentHost(() => import('../components/ui-light-editor/ui-light-editor.js'
255
258
  :aria-label="guarded"
256
259
  ref="sourceElement"
257
260
  >
261
+ <span class="source-glyph" data-hn-ui-light-editor><UiIcon
262
+ class="ui-icon"
263
+ name="lightbulb"
264
+ /></span>
258
265
  </button></span></span>{{ ' ' }}<span
259
266
  class="coordinates"
260
267
  data-hn-ui-light-editor
@@ -293,7 +300,8 @@ useComponentHost(() => import('../components/ui-light-editor/ui-light-editor.js'
293
300
  },
294
301
  }"
295
302
  /></span>{{ ' ' }}<span class="hint" data-hn-ui-light-editor
296
- >Drag the light, use arrow keys, or enter its position. Top centre is 50%, 0%.</span></span>
303
+ >Drag the light, tap the background, use arrow keys, or enter its position. Escape cancels a
304
+ drag. Top centre is 50%, 0%.</span></span>
297
305
  </template>
298
306
 
299
307
  <style scoped>
@@ -354,10 +362,18 @@ useComponentHost(() => import('../components/ui-light-editor/ui-light-editor.js'
354
362
  cursor: grab;
355
363
  touch-action: none;
356
364
  }
365
+ :global(.source-glyph:where([data-hn-ui-light-editor])) {
366
+ position: relative;
367
+ z-index: 1;
368
+ display: inline-flex !important;
369
+ color: var(--ui-accent);
370
+ font-size: var(--ui-font-size-lg);
371
+ }
357
372
  :global(.source:where([data-hn-ui-light-editor])::after) {
358
373
  content: "";
359
- inline-size: 1rem;
360
- block-size: 1rem;
374
+ position: absolute;
375
+ inline-size: 2rem;
376
+ block-size: 2rem;
361
377
  border: var(--ui-border-width) solid var(--ui-accent);
362
378
  border-radius: 50%;
363
379
  background: var(--ui-surface-raised);
@@ -390,6 +406,9 @@ useComponentHost(() => import('../components/ui-light-editor/ui-light-editor.js'
390
406
  color: var(--ui-text-secondary);
391
407
  }
392
408
  @media (forced-colors: active) {
409
+ :global(.source-glyph:where([data-hn-ui-light-editor])) {
410
+ color: CanvasText;
411
+ }
393
412
  :global(.source:where([data-hn-ui-light-editor])::after) {
394
413
  background: Canvas;
395
414
  border-color: CanvasText;
package/vue/UiSelect.vue CHANGED
@@ -386,7 +386,7 @@ useComponentHost(() => import('../components/ui-select/ui-select.js'), {
386
386
  }
387
387
 
388
388
  :global(:scope[data-ui-select-state~="size=sm"]:where([data-hn-ui-select])) {
389
- min-block-size: var(--ui-control-min-block-size);
389
+ min-block-size: max(var(--ui-control-size-sm), var(--ui-control-min-block-size));
390
390
  font-size: inherit;
391
391
  }
392
392
  }
@@ -866,6 +866,6 @@ var UiButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
866
866
  }]]);
867
867
  };
868
868
  }
869
- }), [["__scopeId", "data-v-bb40b79e"]]);
869
+ }), [["__scopeId", "data-v-bf2aad19"]]);
870
870
  //#endregion
871
871
  export { UiButton_default as t };
@@ -321,6 +321,6 @@ var UiColorPicker_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
321
321
  }]]);
322
322
  };
323
323
  }
324
- }), [["__scopeId", "data-v-908b48a2"]]);
324
+ }), [["__scopeId", "data-v-b2a9e21e"]]);
325
325
  //#endregion
326
326
  export { UiColorPicker_default as t };
@@ -2134,6 +2134,6 @@ var UiCombobox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
2134
2134
  }]]);
2135
2135
  };
2136
2136
  }
2137
- }), [["__scopeId", "data-v-ee406b40"]]);
2137
+ }), [["__scopeId", "data-v-85d2ac14"]]);
2138
2138
  //#endregion
2139
2139
  export { UiCombobox_default as t };
@@ -37,7 +37,8 @@ var UiCurveEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
37
37
  emits: [
38
38
  "update:value",
39
39
  "input",
40
- "change"
40
+ "change",
41
+ "cancel"
41
42
  ],
42
43
  setup(__props, { emit: __emit }) {
43
44
  const propValidityContract = { props: {
@@ -164,7 +165,8 @@ var UiCurveEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
164
165
  const dispatch = createDispatch(root, emit, {
165
166
  checks: {
166
167
  input: isInputDetail,
167
- change: isInputDetail
168
+ change: isInputDetail,
169
+ cancel: isInputDetail
168
170
  },
169
171
  modeled: ["value"]
170
172
  });
@@ -408,6 +410,6 @@ var UiCurveEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
408
410
  }]]);
409
411
  };
410
412
  }
411
- }), [["__scopeId", "data-v-b4dfbbee"]]);
413
+ }), [["__scopeId", "data-v-0eaa205c"]]);
412
414
  //#endregion
413
415
  export { UiCurveEditor_default as t };
@@ -704,6 +704,6 @@ var UiInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
704
704
  }]]);
705
705
  };
706
706
  }
707
- }), [["__scopeId", "data-v-c67ac937"]]);
707
+ }), [["__scopeId", "data-v-881962d1"]]);
708
708
  //#endregion
709
709
  export { UiInput_default as t };
@@ -1,5 +1,6 @@
1
1
  import { a as concat, b as useReflectedProp, i as checkHydratedRoot, m as nativeAttrs, o as createDispatch, t as _plugin_vue_export_helper_default, y as useComponentHost } from "./_plugin-vue_export-helper.js";
2
2
  import { d as requireInputStatus, i as checkPropInput, m as vuePropInputs, p as vPropValidity, r as acceptsControllerWrite, t as BINDING_INPUTS_PROP, u as propValidityState } from "./props.js";
3
+ import { t as UiIcon_default } from "./UiIcon.js";
3
4
  import { t as UiNumberInput_default } from "./UiNumberInput.js";
4
5
  import { computed, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, getCurrentInstance, mergeProps, openBlock, provide, ref, renderSlot, toDisplayString, unref, useTemplateRef, watchSyncEffect, withDirectives } from "vue";
5
6
  //#region .build/vue/UiLightEditor.vue?vue&type=script&setup=true&lang.ts
@@ -17,6 +18,10 @@ var _hoisted_3 = {
17
18
  };
18
19
  var _hoisted_4 = ["disabled", "aria-label"];
19
20
  var _hoisted_5 = {
21
+ class: "source-glyph",
22
+ "data-hn-ui-light-editor": ""
23
+ };
24
+ var _hoisted_6 = {
20
25
  class: "coordinates",
21
26
  "data-hn-ui-light-editor": ""
22
27
  };
@@ -37,7 +42,8 @@ var UiLightEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
37
42
  "update:x",
38
43
  "update:y",
39
44
  "input",
40
- "change"
45
+ "change",
46
+ "cancel"
41
47
  ],
42
48
  setup(__props, { emit: __emit }) {
43
49
  const propValidityContract = { props: {
@@ -178,7 +184,8 @@ var UiLightEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
178
184
  const dispatch = createDispatch(root, emit, {
179
185
  checks: {
180
186
  input: isInputDetail,
181
- change: isInputDetail
187
+ change: isInputDetail,
188
+ cancel: isInputDetail
182
189
  },
183
190
  modeled: ["x", "y"]
184
191
  });
@@ -275,10 +282,13 @@ var UiLightEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
275
282
  "aria-label": guarded.value,
276
283
  ref_key: "sourceElement",
277
284
  ref: sourceElement
278
- }, null, 8, _hoisted_4)
285
+ }, [createElementVNode("span", _hoisted_5, [createVNode(UiIcon_default, {
286
+ class: "ui-icon",
287
+ name: "lightbulb"
288
+ })])], 8, _hoisted_4)
279
289
  ], 512)], 8, _hoisted_2),
280
290
  _cache[4] || (_cache[4] = createTextVNode(toDisplayString(" "), -1)),
281
- createElementVNode("span", _hoisted_5, [
291
+ createElementVNode("span", _hoisted_6, [
282
292
  createVNode(UiNumberInput_default, mergeProps({
283
293
  class: "ui-number-input",
284
294
  "data-coordinate": "x",
@@ -335,13 +345,13 @@ var UiLightEditor_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
335
345
  _cache[6] || (_cache[6] = createElementVNode("span", {
336
346
  class: "hint",
337
347
  "data-hn-ui-light-editor": ""
338
- }, "Drag the light, use arrow keys, or enter its position. Top centre is 50%, 0%.", -1))
348
+ }, "Drag the light, tap the background, use arrow keys, or enter its position. Escape cancels a drag. Top centre is 50%, 0%.", -1))
339
349
  ], 16, _hoisted_1)), [[unref(vPropValidity), {
340
350
  contract: propValidityContract,
341
351
  values: { ...props }
342
352
  }]]);
343
353
  };
344
354
  }
345
- }), [["__scopeId", "data-v-1cd5c26a"]]);
355
+ }), [["__scopeId", "data-v-dc22fbcf"]]);
346
356
  //#endregion
347
357
  export { UiLightEditor_default as t };
@@ -221,6 +221,6 @@ var UiSelect_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
221
221
  }]]);
222
222
  };
223
223
  }
224
- }), [["__scopeId", "data-v-4348aae0"]]);
224
+ }), [["__scopeId", "data-v-1d116383"]]);
225
225
  //#endregion
226
226
  export { UiSelect_default as t };