@threadlabs/looma 0.13.6 → 0.14.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 (60) hide show
  1. package/components/ui-button/ui-button.html +2 -1
  2. package/components/ui-checkbox/ui-checkbox.html +35 -0
  3. package/components/ui-combobox/ui-combobox.html +38 -3
  4. package/components/ui-input/ui-input.html +27 -0
  5. package/components/ui-input-group/ui-input-group.html +15 -0
  6. package/components/ui-radio/ui-radio.html +35 -0
  7. package/components/ui-select/ui-select.html +29 -3
  8. package/components/ui-switch/ui-switch.html +36 -0
  9. package/components/ui-textarea/ui-textarea.html +20 -0
  10. package/dist/index.js +1 -1
  11. package/package.json +1 -1
  12. package/styles/ui-button.css +3 -1
  13. package/styles/ui-checkbox.css +38 -0
  14. package/styles/ui-combobox.css +38 -1
  15. package/styles/ui-input-group.css +20 -0
  16. package/styles/ui-input.css +29 -0
  17. package/styles/ui-radio.css +38 -0
  18. package/styles/ui-select.css +29 -3
  19. package/styles/ui-switch.css +41 -0
  20. package/styles/ui-textarea.css +21 -0
  21. package/vanilla/UiCheckbox.d.ts +1 -0
  22. package/vanilla/UiCheckbox.js +1 -1
  23. package/vanilla/UiCombobox.d.ts +1 -1
  24. package/vanilla/UiCombobox.js +1 -1
  25. package/vanilla/UiInput.d.ts +1 -0
  26. package/vanilla/UiInput.js +1 -1
  27. package/vanilla/UiRadio.d.ts +1 -0
  28. package/vanilla/UiRadio.js +1 -1
  29. package/vanilla/UiSelect.d.ts +1 -0
  30. package/vanilla/UiSelect.js +1 -1
  31. package/vanilla/UiSwitch.d.ts +1 -0
  32. package/vanilla/UiSwitch.js +1 -1
  33. package/vanilla/UiTextarea.d.ts +1 -0
  34. package/vanilla/UiTextarea.js +1 -1
  35. package/vue/UiButton.vue +2 -1
  36. package/vue/UiCheckbox.d.ts +2 -0
  37. package/vue/UiCheckbox.vue +34 -0
  38. package/vue/UiCombobox.d.ts +2 -2
  39. package/vue/UiCombobox.vue +39 -2
  40. package/vue/UiInput.d.ts +2 -0
  41. package/vue/UiInput.vue +41 -0
  42. package/vue/UiInputGroup.vue +15 -0
  43. package/vue/UiRadio.d.ts +2 -0
  44. package/vue/UiRadio.vue +34 -0
  45. package/vue/UiSelect.d.ts +2 -0
  46. package/vue/UiSelect.vue +34 -3
  47. package/vue/UiSwitch.d.ts +2 -0
  48. package/vue/UiSwitch.vue +35 -0
  49. package/vue/UiTextarea.d.ts +2 -0
  50. package/vue/UiTextarea.vue +27 -0
  51. package/vue/chunks/UiButton.js +1 -1
  52. package/vue/chunks/UiCheckbox.js +3 -2
  53. package/vue/chunks/UiCombobox.js +1 -1
  54. package/vue/chunks/UiInput.js +7 -2
  55. package/vue/chunks/UiInputGroup.js +1 -1
  56. package/vue/chunks/UiRadio.js +3 -2
  57. package/vue/chunks/UiSelect.js +7 -2
  58. package/vue/chunks/UiSwitch.js +3 -2
  59. package/vue/chunks/UiTextarea.js +7 -2
  60. package/vue/components.css +365 -165
@@ -316,8 +316,9 @@
316
316
  border-color: transparent;
317
317
  }
318
318
 
319
+ /* The size states outrank :host, so sm is named too, or a small button would stay small. */
319
320
  @media (pointer: coarse) {
320
- :host {
321
+ :host, :host-state([size="sm"]) {
321
322
  min-block-size: var(--ui-control-min-block-size);
322
323
  }
323
324
  }
@@ -14,6 +14,10 @@
14
14
  <prop name="value" type="string" default="on"
15
15
  >The value a form submits under name while the checkbox is checked, also reported in the change event's detail so one handler can tell checkboxes apart. on
16
16
  by default, as on a native checkbox.</prop>
17
+ <prop name="size" type="sm | md | lg" default="md"
18
+ >Aligns the checkbox with fields and buttons of the same size. It keeps its box size; the label takes that size's field type size and its first line centres
19
+ in that control height (sm 2rem, lg 3rem by default), so in a row of inputs, selects, and buttons of one size it shares their height and their text
20
+ baseline. Under a coarse pointer an sm row grows to the 44px touch minimum, as they do. md is the standard layout.</prop>
17
21
  <state name="internalChecked" :value="false"></state>
18
22
  <event name="change" type="object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })"></event>
19
23
  </defs>
@@ -154,6 +158,37 @@
154
158
  cursor: not-allowed;
155
159
  }
156
160
 
161
+ /* A sized choice keeps its box and aligns instead: the label takes that size's field type size,
162
+ and the first line centres in that control height, so the label shares a row's baseline.
163
+ Multi-line labels and descriptions still grow downward from that first line. */
164
+ :host-state([size="sm"]) {
165
+ --_font: var(--ui-font-size-sm);
166
+ --_row: var(--ui-control-size-sm);
167
+ }
168
+
169
+ :host-state([size="lg"]) {
170
+ --_font: var(--ui-font-size-md);
171
+ --_row: var(--ui-control-size-lg);
172
+ }
173
+
174
+ :host-state([size="sm"]),
175
+ :host-state([size="lg"]) {
176
+ --_line: calc(var(--_font) * var(--ui-line-height-md));
177
+ font-size: var(--_font);
178
+ padding-block: calc((var(--_row) - var(--_line)) / 2);
179
+ }
180
+
181
+ :host-state([size="sm"]) input,
182
+ :host-state([size="lg"]) input {
183
+ margin-top: calc((var(--_line) - var(--_ui-checkbox-size, 1.125rem)) / 2);
184
+ }
185
+
186
+ @media (pointer: coarse) {
187
+ :host-state([size="sm"]) {
188
+ --_row: var(--ui-control-min-block-size);
189
+ }
190
+ }
191
+
157
192
  /* A component that sets its display still honours the hidden attribute on its root. */
158
193
  :host[hidden] {
159
194
  display: none;
@@ -44,8 +44,10 @@
44
44
  <prop name="required" type="boolean" default="false"
45
45
  >Requires a value: adds an asterisk to the label, marks the input required for native form validation, and makes validation report "A value is required."
46
46
  when the field is left empty. In multiple mode a chosen item satisfies it, whatever is left in the input.</prop>
47
- <prop name="size" type="sm | md" default="md"
48
- >Field height. md is the standard control height; sm is the small control height with tighter padding, for dense forms.</prop>
47
+ <prop name="size" type="sm | md | lg" default="md"
48
+ >Field height, padding, and type size, matching Button's sizes so a field and a button of one size share a row. sm is the small control height (2rem by
49
+ default) with smaller text, for compact rows such as a table row or a filter bar; md is the standard control height; lg is taller with roomier padding.
50
+ Under a coarse pointer, sm grows to the 44px touch minimum and keeps body-size text. The label keeps its size.</prop>
49
51
  <prop name="labelVisibility" type="visible | sr-only" default="visible"
50
52
  >visible shows the label above the field; sr-only hides it visually but keeps it as the input's accessible name. Use sr-only only when the surrounding
51
53
  layout already makes the field's purpose clear.</prop>
@@ -395,9 +397,18 @@
395
397
  margin: 0;
396
398
  outline: none;
397
399
  }
400
+ :host-state([size="sm"]) {
401
+ --_row: var(--ui-control-size-sm);
402
+ }
398
403
  :host-state([size="sm"]) input {
399
- min-block-size: calc(var(--ui-control-size-sm) - 2px);
404
+ min-block-size: calc(var(--_row) - 2px);
400
405
  padding: var(--ui-space-1) var(--ui-space-2);
406
+ font-size: var(--ui-font-size-sm);
407
+ }
408
+ :host-state([size="lg"]) input {
409
+ min-block-size: calc(var(--ui-control-size-lg) - 2px);
410
+ padding: var(--ui-space-3) var(--ui-space-4);
411
+ font-size: var(--ui-font-size-md);
401
412
  }
402
413
  button {
403
414
  appearance: none;
@@ -498,6 +509,14 @@
498
509
  min-inline-size: 2.75rem;
499
510
  min-block-size: 2.75rem;
500
511
  }
512
+ /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
513
+ body-size text: below 16px, iOS zooms the page into the focused field. */
514
+ :host-state([size="sm"]) {
515
+ --_row: var(--ui-control-min-block-size);
516
+ }
517
+ :host-state([size="sm"]) input {
518
+ font-size: inherit;
519
+ }
501
520
  }
502
521
  @media (forced-colors: active) {
503
522
  .field, .popup {
@@ -555,6 +574,22 @@
555
574
  min-block-size: 1.75rem;
556
575
  padding: var(--ui-space-0-5) var(--ui-space-1);
557
576
  }
577
+ /* A small multiple field starts at the small control height; chosen items still wrap it taller. */
578
+ :host-state([multiple]):host-state([size="sm"]) .field {
579
+ min-block-size: 0;
580
+ padding: var(--ui-space-0-5);
581
+ }
582
+ :host-state([multiple]):host-state([size="sm"]) input {
583
+ min-block-size: calc(var(--_row) - 2px - 2 * var(--ui-space-0-5));
584
+ padding: 0 var(--ui-space-1);
585
+ }
586
+ /* A large multiple field starts at the large control height, as sm starts at the small one. */
587
+ :host-state([multiple]):host-state([size="lg"]) .field {
588
+ min-block-size: 0;
589
+ }
590
+ :host-state([multiple]):host-state([size="lg"]) input {
591
+ min-block-size: calc(var(--ui-control-size-lg) - 2px - 2 * var(--ui-space-1));
592
+ }
558
593
  .item {
559
594
  appearance: none;
560
595
  display: inline-flex;
@@ -7,6 +7,11 @@
7
7
  <prop name="invalid" type="boolean" default="false">Exposes invalid state to assistive technology and styling.</prop>
8
8
  <prop name="readonly" type="boolean" default="false">Allows selection without allowing edits.</prop>
9
9
  <prop name="required" type="boolean" default="false">Marks the control as required for form validation.</prop>
10
+ <prop name="size" type="sm | md | lg" default="md"
11
+ >Height, padding, and type size, matching Button's sizes so a field and a button of one size share a row. sm is the small control height (2rem by default)
12
+ with smaller text, for compact rows such as a table row or a filter bar; md is the standard control height; lg is taller with roomier padding. Under a
13
+ coarse pointer, sm grows to the 44px touch minimum and keeps body-size text. Change it after render through this option; the element's size property is
14
+ the native one.</prop>
10
15
  </defs>
11
16
  <input :value="value" :disabled="disabled" :readonly="readonly" :required="required" :aria-invalid="invalid">
12
17
  <style>
@@ -84,6 +89,28 @@
84
89
  background-color var(--ui-motion-fast) var(--ui-motion-ease);
85
90
  }
86
91
 
92
+ /* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
93
+ :host-state([size="sm"]) {
94
+ min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-sm)));
95
+ padding: var(--ui-space-1) var(--ui-space-2);
96
+ font-size: var(--ui-font-size-sm);
97
+ }
98
+
99
+ :host-state([size="lg"]) {
100
+ min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-lg)));
101
+ padding: var(--ui-space-3) var(--ui-space-4);
102
+ font-size: var(--ui-font-size-md);
103
+ }
104
+
105
+ /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
106
+ body-size text: below 16px, iOS zooms the page into the focused field. */
107
+ @media (pointer: coarse) {
108
+ :host-state([size="sm"]) {
109
+ min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-min-block-size)));
110
+ font-size: inherit;
111
+ }
112
+ }
113
+
87
114
  /* Tabbing into a field selects its text; tint that selection softly instead of the page highlight. */
88
115
  :host::selection {
89
116
  background: color-mix(in srgb, var(--ui-accent-solid) 20%, transparent);
@@ -50,6 +50,21 @@
50
50
  transition: border-color var(--ui-motion-fast) var(--ui-motion-ease), box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
51
51
  }
52
52
 
53
+ /* The frame takes its input's size, so a small group lines up with small buttons and fields. */
54
+ :host:has(.field [data-ui-input-state~="size=sm"]) {
55
+ min-block-size: var(--ui-control-size-sm);
56
+ }
57
+
58
+ :host:has(.field [data-ui-input-state~="size=lg"]) {
59
+ min-block-size: var(--ui-control-size-lg);
60
+ }
61
+
62
+ @media (pointer: coarse) {
63
+ :host:has(.field [data-ui-input-state~="size=sm"]) {
64
+ min-block-size: var(--ui-control-min-block-size);
65
+ }
66
+ }
67
+
53
68
  :host:hover:not(:has(input:focus, input:disabled)) {
54
69
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
55
70
  }
@@ -12,6 +12,10 @@
12
12
  <prop name="value" type="string" default="on"
13
13
  >The value this radio stands for: submitted with a form, reported in its change event, and matched against a ui-radio-group's value to decide which radio is
14
14
  checked.</prop>
15
+ <prop name="size" type="sm | md | lg" default="md"
16
+ >Aligns the radio with fields and buttons of the same size. It keeps its box size; the label takes that size's field type size and its first line centres in
17
+ that control height (sm 2rem, lg 3rem by default), so in a row of inputs, selects, and buttons of one size it shares their height and their text baseline.
18
+ Under a coarse pointer an sm row grows to the 44px touch minimum, as they do. md is the standard layout.</prop>
15
19
  <state name="internalChecked" :value="false"></state>
16
20
  <event name="change" type="object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })"></event>
17
21
  </defs>
@@ -90,6 +94,37 @@
90
94
  cursor: not-allowed;
91
95
  }
92
96
 
97
+ /* A sized choice keeps its box and aligns instead: the label takes that size's field type size,
98
+ and the first line centres in that control height, so the label shares a row's baseline.
99
+ Multi-line labels and descriptions still grow downward from that first line. */
100
+ :host-state([size="sm"]) {
101
+ --_font: var(--ui-font-size-sm);
102
+ --_row: var(--ui-control-size-sm);
103
+ }
104
+
105
+ :host-state([size="lg"]) {
106
+ --_font: var(--ui-font-size-md);
107
+ --_row: var(--ui-control-size-lg);
108
+ }
109
+
110
+ :host-state([size="sm"]),
111
+ :host-state([size="lg"]) {
112
+ --_line: calc(var(--_font) * var(--ui-line-height-md));
113
+ font-size: var(--_font);
114
+ padding-block: calc((var(--_row) - var(--_line)) / 2);
115
+ }
116
+
117
+ :host-state([size="sm"]) input,
118
+ :host-state([size="lg"]) input {
119
+ margin-top: calc((var(--_line) - var(--_ui-radio-size, 1.125rem)) / 2);
120
+ }
121
+
122
+ @media (pointer: coarse) {
123
+ :host-state([size="sm"]) {
124
+ --_row: var(--ui-control-min-block-size);
125
+ }
126
+ }
127
+
93
128
  /* A component that sets its display still honours the hidden attribute on its root. */
94
129
  :host[hidden] {
95
130
  display: none;
@@ -6,6 +6,11 @@
6
6
  <prop name="disabled" type="boolean" default="false">Prevents selection and form submission.</prop>
7
7
  <prop name="invalid" type="boolean" default="false">Exposes invalid state to assistive technology and styling.</prop>
8
8
  <prop name="required" type="boolean" default="false">Requires a non-empty option for form validation.</prop>
9
+ <prop name="size" type="sm | md | lg" default="md"
10
+ >Height, padding, chevron, and type size, matching Button's sizes so a field and a button of one size share a row. sm is the small control height (2rem by
11
+ default) with smaller text and a smaller chevron, for compact rows such as a table row or a filter bar; md is the standard control height; lg is taller
12
+ with roomier padding. Under a coarse pointer, sm grows to the 44px touch minimum and keeps body-size text. It is not the native size attribute: a select
13
+ is always a single-choice dropdown. Change it after render through this option; the element's size property is the native one.</prop>
9
14
  </defs>
10
15
  <select :disabled="disabled" :required="required" :aria-invalid="invalid"><slot></slot></select>
11
16
  <style>
@@ -88,9 +93,30 @@
88
93
  cursor: not-allowed;
89
94
  }
90
95
 
91
- :host[size]:not([size="1"]) {
92
- min-block-size: 7rem;
93
- padding: var(--ui-space-2);
96
+ :host-state([size="sm"]) {
97
+ min-block-size: var(--ui-control-size-sm);
98
+ padding: var(--ui-space-1) var(--ui-space-2);
99
+ padding-inline-end: calc(var(--ui-space-2) + var(--ui-icon-size-sm) + var(--ui-space-1));
100
+ background-position: right var(--ui-space-2) center;
101
+ background-size: var(--ui-icon-size-sm) var(--ui-icon-size-sm);
102
+ font-size: var(--ui-font-size-sm);
103
+ }
104
+
105
+ :host-state([size="lg"]) {
106
+ min-block-size: var(--ui-control-size-lg);
107
+ padding: var(--ui-space-3) var(--ui-space-4);
108
+ padding-inline-end: calc(var(--ui-space-4) + 1.5rem);
109
+ background-position: right var(--ui-space-4) center;
110
+ font-size: var(--ui-font-size-md);
111
+ }
112
+
113
+ /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
114
+ body-size text: below 16px, iOS zooms the page into the focused field. */
115
+ @media (pointer: coarse) {
116
+ :host-state([size="sm"]) {
117
+ min-block-size: var(--ui-control-min-block-size);
118
+ font-size: inherit;
119
+ }
94
120
  }
95
121
 
96
122
  /* A component that sets its display still honours the hidden attribute on its root. */
@@ -9,6 +9,10 @@
9
9
  <prop name="value" type="string" default="on"
10
10
  >The value a form submits under name while the switch is on, also reported in the change event's detail so one handler can tell switches apart. on by
11
11
  default, as on a native checkbox.</prop>
12
+ <prop name="size" type="sm | md | lg" default="md"
13
+ >Aligns the switch with fields and buttons of the same size. It keeps its track size; the label takes that size's field type size and its first line centres
14
+ in that control height (sm 2rem, lg 3rem by default), so in a row of inputs, selects, and buttons of one size it shares their height and their text
15
+ baseline. Under a coarse pointer an sm row grows to the 44px touch minimum, as they do. md is the standard layout.</prop>
12
16
  <state name="internalChecked" :value="false"></state>
13
17
  <event name="change" type="object({ checked: boolean, value: string, trigger: keyboard | pointer | programmatic })"></event>
14
18
  </defs>
@@ -131,6 +135,38 @@
131
135
  }
132
136
  }
133
137
 
138
+ /* A sized switch keeps its track and aligns instead: the label takes that size's field type size,
139
+ and the first line centres in that control height, so the label shares a row's baseline.
140
+ Multi-line labels and descriptions still grow downward from that first line. */
141
+ :host-state([size="sm"]) {
142
+ --_font: var(--ui-font-size-sm);
143
+ --_row: var(--ui-control-size-sm);
144
+ }
145
+
146
+ :host-state([size="lg"]) {
147
+ --_font: var(--ui-font-size-md);
148
+ --_row: var(--ui-control-size-lg);
149
+ }
150
+
151
+ :host-state([size="sm"]),
152
+ :host-state([size="lg"]) {
153
+ --_line: calc(var(--_font) * var(--ui-line-height-md));
154
+ font-size: var(--_font);
155
+ padding-block: calc((var(--_row) - var(--ui-switch-track-height, 1.5rem)) / 2);
156
+ }
157
+
158
+ /* The label centres on the track. */
159
+ :host-state([size="sm"]) .text,
160
+ :host-state([size="lg"]) .text {
161
+ margin-top: calc((var(--_ui-switch-track-height, 1.5rem) - var(--_line)) / 2);
162
+ }
163
+
164
+ @media (pointer: coarse) {
165
+ :host-state([size="sm"]) {
166
+ --_row: var(--ui-control-min-block-size);
167
+ }
168
+ }
169
+
134
170
  /* A component that sets its display still honours the hidden attribute on its root. */
135
171
  :host[hidden] {
136
172
  display: none;
@@ -8,6 +8,9 @@
8
8
  <prop name="readonly" type="boolean" default="false">Allows selection without allowing edits.</prop>
9
9
  <prop name="required" type="boolean" default="false">Marks the control as required for form validation.</prop>
10
10
  <prop name="rows" type="integer" default="4">Initial number of visible text rows.</prop>
11
+ <prop name="size" type="sm | md | lg" default="md"
12
+ >Padding and type size, matching Input's sizes so a textarea sits with fields of the same size; rows sets its height. sm has tighter padding and smaller
13
+ text, for compact forms; lg has roomier padding. Under a coarse pointer, sm keeps body-size text.</prop>
11
14
  </defs>
12
15
  <textarea :value="value" :disabled="disabled" :readonly="readonly" :required="required" :aria-invalid="invalid" :rows="rows"></textarea>
13
16
  <style>
@@ -88,6 +91,23 @@
88
91
  cursor: not-allowed;
89
92
  }
90
93
 
94
+ :host-state([size="sm"]) {
95
+ padding: var(--ui-space-1) var(--ui-space-2);
96
+ font-size: var(--ui-font-size-sm);
97
+ }
98
+
99
+ :host-state([size="lg"]) {
100
+ padding: var(--ui-space-3) var(--ui-space-4);
101
+ font-size: var(--ui-font-size-md);
102
+ }
103
+
104
+ /* Below 16px, iOS zooms the page into the focused field, so touch keeps body-size text. */
105
+ @media (pointer: coarse) {
106
+ :host-state([size="sm"]) {
107
+ font-size: inherit;
108
+ }
109
+ }
110
+
91
111
  /* A component that sets its display still honours the hidden attribute on its root. */
92
112
  :host[hidden] {
93
113
  display: none;