@threadlabs/looma 0.25.0 → 0.25.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -35,6 +35,17 @@
35
35
  --_ui-default-input-focus-outline: none;
36
36
  --_ui-default-input-radius: 0;
37
37
  --_ui-default-input-surface: transparent;
38
+ --_ui-default-textarea-border-width: 0;
39
+ --_ui-default-textarea-border: transparent;
40
+ --_ui-default-textarea-border-hover: transparent;
41
+ --_ui-default-textarea-focus-border: transparent;
42
+ --_ui-default-textarea-invalid-border: transparent;
43
+ --_ui-default-textarea-invalid-focus-shadow: none;
44
+ --_ui-default-textarea-shadow: none;
45
+ --_ui-default-textarea-focus-shadow: none;
46
+ --_ui-default-textarea-focus-outline: none;
47
+ --_ui-default-textarea-radius: 0;
48
+ --_ui-default-textarea-surface: transparent;
38
49
 
39
50
  display: flex;
40
51
  align-items: stretch;
@@ -68,13 +79,13 @@
68
79
  }
69
80
  }
70
81
 
71
- :scope:hover:not(:has(input:focus, input:disabled, input:read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))) {
82
+ :scope:hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))) {
72
83
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
73
84
  }
74
85
 
75
86
  /* The ring shows when the field's input would show its own: when it is focus-visible, which a
76
87
  text input is for a pointer as well as a keyboard. A focused action button draws its own. */
77
- :scope:has(.field input:focus-visible) {
88
+ :scope:has(.field :is(input, textarea):focus-visible) {
78
89
  border-color: var(--ui-control-focus, var(--ui-accent-solid));
79
90
  box-shadow:
80
91
  var(--ui-control-inset),
@@ -85,7 +96,7 @@
85
96
  border-color: var(--ui-input-group-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)));
86
97
  }
87
98
 
88
- :scope:has(input:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible) {
99
+ :scope:has(:is(input, textarea):is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible) {
89
100
  box-shadow: var(
90
101
  --ui-input-group-invalid-focus-shadow,
91
102
  var(--ui-control-inset),
@@ -99,14 +110,14 @@
99
110
 
100
111
  /* Forced colors drop box shadows, so the focus ring becomes an outline. */
101
112
  @media (forced-colors: active) {
102
- :scope:has(input:focus-visible) {
113
+ :scope:has(:is(input, textarea):focus-visible) {
103
114
  outline: var(--ui-focus-width) solid Highlight;
104
115
  outline-offset: var(--ui-border-width);
105
116
  }
106
117
  }
107
118
 
108
119
  :scope[data-ui-input-group-state~="disabled"],
109
- :scope:has(input:disabled) {
120
+ :scope:has(:is(input, textarea):disabled) {
110
121
  background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
111
122
  box-shadow: none;
112
123
  cursor: not-allowed;
@@ -161,6 +172,23 @@
161
172
  padding-inline: var(--ui-space-1);
162
173
  }
163
174
 
175
+ /* Multiline text owns the first row; its action remains inside the same frame below it. */
176
+ :scope[data-ui-input-group-state~="multiline"] {
177
+ display: grid;
178
+ grid-template-columns: auto minmax(0, 1fr) auto;
179
+ }
180
+
181
+ :scope[data-ui-input-group-state~="multiline"] .field {
182
+ grid-column: 2;
183
+ }
184
+
185
+ :scope[data-ui-input-group-state~="multiline"] .action {
186
+ grid-column: 1 / -1;
187
+ justify-content: end;
188
+ padding: var(--ui-space-2);
189
+ padding-block-start: 0;
190
+ }
191
+
164
192
  .affix:empty,
165
193
  .action:empty {
166
194
  display: none;
@@ -198,5 +226,7 @@
198
226
  /* Centred rather than padded above and below: the group is exactly a lone Input's height, so a
199
227
  small button sits inside it with the difference split evenly, and the field keeps its height. */
200
228
 
229
+ /* Multiline text owns the first row; its action remains inside the same frame below it. */
230
+
201
231
  /* A component that sets its display still honours the hidden attribute on its root. */
202
232
  }
@@ -1,4 +1,28 @@
1
1
  /* Generated by HTML Next 1.0.0-alpha.29. Do not edit. */
2
+ @property --ui-textarea-border-width {
3
+ syntax: "*";
4
+ inherits: false;
5
+ }
6
+ @property --ui-textarea-border {
7
+ syntax: "*";
8
+ inherits: false;
9
+ }
10
+ @property --ui-textarea-border-hover {
11
+ syntax: "*";
12
+ inherits: false;
13
+ }
14
+ @property --ui-textarea-focus-border {
15
+ syntax: "*";
16
+ inherits: false;
17
+ }
18
+ @property --ui-textarea-focus-outline {
19
+ syntax: "*";
20
+ inherits: false;
21
+ }
22
+ @property --ui-textarea-surface {
23
+ syntax: "*";
24
+ inherits: false;
25
+ }
2
26
  @property --ui-textarea-focus-shadow {
3
27
  syntax: "*";
4
28
  inherits: false;
@@ -33,13 +57,14 @@
33
57
  padding: var(--ui-space-2) var(--ui-space-3);
34
58
  appearance: none;
35
59
  resize: vertical;
36
- border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
37
- border-radius: var(--ui-textarea-radius, var(--ui-field-radius, var(--ui-radius-md)));
38
- background: var(--ui-control-surface, var(--ui-surface-elevated));
60
+ border: var(--ui-textarea-border-width, var(--_ui-default-textarea-border-width, var(--ui-border-width))) solid
61
+ var(--ui-textarea-border, var(--_ui-default-textarea-border, var(--ui-control-border, var(--ui-border-strong))));
62
+ border-radius: var(--ui-textarea-radius, var(--_ui-default-textarea-radius, var(--ui-field-radius, var(--ui-radius-md))));
63
+ background: var(--ui-textarea-surface, var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated))));
39
64
  color: var(--ui-text-primary);
40
65
  font: inherit;
41
66
  line-height: var(--ui-line-height-md);
42
- box-shadow: var(--ui-textarea-shadow, var(--ui-control-inset));
67
+ box-shadow: var(--ui-textarea-shadow, var(--_ui-default-textarea-shadow, var(--ui-control-inset)));
43
68
  transition:
44
69
  border-color var(--ui-motion-fast) var(--ui-motion-ease),
45
70
  box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
@@ -58,29 +83,32 @@
58
83
  }
59
84
 
60
85
  :scope:hover:not(:focus):not(:disabled):not(:read-only):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])) {
61
- border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
86
+ border-color: var(--ui-textarea-border-hover, var(--_ui-default-textarea-border-hover, var(--ui-control-border-hover, var(--ui-text-secondary))));
62
87
  }
63
88
 
64
89
  :scope:focus-visible {
65
- border-color: var(--ui-control-focus, var(--ui-accent-solid));
66
- outline: none;
67
- box-shadow: var(--ui-textarea-focus-shadow, var(--ui-control-inset), var(--ui-control-focus-halo-shadow));
90
+ border-color: var(--ui-textarea-focus-border, var(--_ui-default-textarea-focus-border, var(--ui-control-focus, var(--ui-accent-solid))));
91
+ outline: var(--ui-textarea-focus-outline, var(--_ui-default-textarea-focus-outline, none));
92
+ box-shadow: var(--ui-textarea-focus-shadow, var(--_ui-default-textarea-focus-shadow, var(--ui-control-inset), var(--ui-control-focus-halo-shadow)));
68
93
  }
69
94
 
70
95
  /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
71
96
  :scope[aria-invalid="true"],
72
97
  :scope:is(:user-invalid, [data-user-invalid]) {
73
- border-color: var(--ui-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)));
98
+ border-color: var(--ui-textarea-invalid-border, var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))));
74
99
  }
75
100
 
76
101
  :scope:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible {
77
102
  box-shadow: var(
78
103
  --ui-textarea-invalid-focus-shadow,
79
- var(--ui-control-inset),
80
- 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
81
- in srgb,
82
- var(--ui-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%,
83
- transparent
104
+ var(
105
+ --_ui-default-textarea-invalid-focus-shadow,
106
+ var(--ui-control-inset),
107
+ 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
108
+ in srgb,
109
+ var(--ui-textarea-invalid-border, var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)))) 22%,
110
+ transparent
111
+ )
84
112
  )
85
113
  );
86
114
  }
@@ -7,6 +7,7 @@ export interface UiInputGroupElement extends HTMLDivElement {
7
7
  }
8
8
  export interface UiInputGroupProps {
9
9
  disabled?: boolean | null;
10
+ multiline?: boolean | null;
10
11
  prefixKind?: "text" | "icon" | null;
11
12
  attributes?: Readonly<Record<string, string | number | boolean | null | undefined>>;
12
13
  children?: readonly (string | Node)[];
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import * as controller from "../components/ui-input-group/ui-input-group.js";
4
4
  import "../styles/ui-input-group.css";
5
5
 
6
- const definition = {...{"contract":{"tag":"ui-input-group","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"prefixKind":{"type":{"kind":"terminal","name":"keyword"},"values":["text","icon"],"required":false,"target":{"attribute":"prefixkind"},"default":"text"}}},"template":{"kind":"element","name":"div","attributes":[],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"affix"}],"children":[{"kind":"slot","fallback":[],"name":"prefix"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"field"}],"children":[{"kind":"slot"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"affix"}],"children":[{"kind":"slot","fallback":[],"name":"suffix"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"action"}],"children":[{"kind":"slot","fallback":[],"name":"action"}]}]},"declarations":[],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
6
+ const definition = {...{"contract":{"tag":"ui-input-group","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"multiline":{"type":"boolean","required":false,"target":{"attribute":"multiline"},"default":false},"prefixKind":{"type":{"kind":"terminal","name":"keyword"},"values":["text","icon"],"required":false,"target":{"attribute":"prefixkind"},"default":"text"}}},"template":{"kind":"element","name":"div","attributes":[],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"affix"}],"children":[{"kind":"slot","fallback":[],"name":"prefix"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"field"}],"children":[{"kind":"slot"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"affix"}],"children":[{"kind":"slot","fallback":[],"name":"suffix"}]},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"action"}],"children":[{"kind":"slot","fallback":[],"name":"action"}]}]},"declarations":[],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
7
7
 
8
8
  export function createUiInputGroup(options = {}) {
9
9
  const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
@@ -14,6 +14,10 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
14
14
  type: PropType<boolean | null>;
15
15
  default: boolean;
16
16
  };
17
+ multiline: {
18
+ type: PropType<boolean | null>;
19
+ default: boolean;
20
+ };
17
21
  prefixKind: {
18
22
  type: PropType<"text" | "icon" | null>;
19
23
  default: string;
@@ -23,12 +27,17 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
23
27
  type: PropType<boolean | null>;
24
28
  default: boolean;
25
29
  };
30
+ multiline: {
31
+ type: PropType<boolean | null>;
32
+ default: boolean;
33
+ };
26
34
  prefixKind: {
27
35
  type: PropType<"text" | "icon" | null>;
28
36
  default: string;
29
37
  };
30
38
  }>> & Readonly<{}>, {
31
39
  disabled: boolean | null;
40
+ multiline: boolean | null;
32
41
  prefixKind: "text" | "icon" | null;
33
42
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
34
43
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -17,6 +17,13 @@ const propValidityContract = {
17
17
  'Fades the group\'s framing to match a disabled input inside it. Disable the input itself too.',
18
18
  default: false,
19
19
  },
20
+ multiline: {
21
+ type: { kind: 'terminal', name: 'boolean' },
22
+ required: false,
23
+ target: { attribute: 'multiline' },
24
+ description: 'Groups a Textarea with an action below its text inside the shared frame.',
25
+ default: false,
26
+ },
20
27
  prefixKind: {
21
28
  type: { kind: 'terminal', name: 'keyword' },
22
29
  required: false,
@@ -30,9 +37,14 @@ const propValidityContract = {
30
37
  } as const
31
38
  const props = defineProps({
32
39
  disabled: { type: null as unknown as PropType<boolean | null>, default: false },
40
+ multiline: { type: null as unknown as PropType<boolean | null>, default: false },
33
41
  prefixKind: { type: null as unknown as PropType<'text' | 'icon' | null>, default: 'text' },
34
42
  })
35
- const acceptedProps: Record<string, unknown> = { disabled: false, prefixKind: 'text' }
43
+ const acceptedProps: Record<string, unknown> = {
44
+ disabled: false,
45
+ multiline: false,
46
+ prefixKind: 'text',
47
+ }
36
48
  const checkedProps = computed(() => ({
37
49
  disabled: checkedProp<boolean>(
38
50
  props['disabled'],
@@ -41,6 +53,13 @@ const checkedProps = computed(() => ({
41
53
  'disabled',
42
54
  acceptedProps,
43
55
  ) as boolean,
56
+ multiline: checkedProp<boolean>(
57
+ props['multiline'],
58
+ { kind: 'terminal', name: 'boolean' },
59
+ false,
60
+ 'multiline',
61
+ acceptedProps,
62
+ ) as boolean,
44
63
  prefixKind: checkedProp<'text' | 'icon'>(
45
64
  props['prefixKind'],
46
65
  { kind: 'terminal', name: 'keyword' },
@@ -112,6 +131,7 @@ const hostState = computed(() =>
112
131
  checkedProps.value.prefixKind && 'prefixKind',
113
132
  checkedProps.value.prefixKind != null
114
133
  && `prefixKind=${encodeURIComponent(checkedProps.value.prefixKind)}`,
134
+ checkedProps.value.multiline && 'multiline',
115
135
  ].filter(Boolean).join(' ')
116
136
  )
117
137
 
@@ -132,7 +152,7 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
132
152
  ? (incoming[name] ?? incoming[kebab] ?? null)
133
153
  : null
134
154
  },
135
- propNames: ['disabled', 'prefixKind'],
155
+ propNames: ['disabled', 'multiline', 'prefixKind'],
136
156
  propValidity: (name: string) =>
137
157
  propValidityState({ contract: propValidityContract, values: { ...props } }, name),
138
158
  })
@@ -144,6 +164,7 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
144
164
  v-bind="nativeAttrs($attrs)"
145
165
  v-prop-validity="{ contract: propValidityContract, values: { ...props } }"
146
166
  :data-disabled="reflectedProp('disabled', 'disabled', checkedProps.disabled, undefined, false)"
167
+ :data-multiline="reflectedProp('multiline', 'multiline', checkedProps.multiline, undefined, false)"
147
168
  :data-prefix-kind="reflectedProp('prefixKind', 'prefix-kind', checkedProps.prefixKind, undefined, false)"
148
169
  :data-ui-input-group-state="hostState || undefined"
149
170
  ref="root"
@@ -191,6 +212,17 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
191
212
  --_ui-default-input-focus-outline: none;
192
213
  --_ui-default-input-radius: 0;
193
214
  --_ui-default-input-surface: transparent;
215
+ --_ui-default-textarea-border-width: 0;
216
+ --_ui-default-textarea-border: transparent;
217
+ --_ui-default-textarea-border-hover: transparent;
218
+ --_ui-default-textarea-focus-border: transparent;
219
+ --_ui-default-textarea-invalid-border: transparent;
220
+ --_ui-default-textarea-invalid-focus-shadow: none;
221
+ --_ui-default-textarea-shadow: none;
222
+ --_ui-default-textarea-focus-shadow: none;
223
+ --_ui-default-textarea-focus-outline: none;
224
+ --_ui-default-textarea-radius: 0;
225
+ --_ui-default-textarea-surface: transparent;
194
226
 
195
227
  display: flex;
196
228
  align-items: stretch;
@@ -226,13 +258,13 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
226
258
  }
227
259
  }
228
260
 
229
- [data-component~="ui-input-group"]:hover:not(:has(input:focus, input:disabled, input:read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))) {
261
+ [data-component~="ui-input-group"]:hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))) {
230
262
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
231
263
  }
232
264
 
233
265
  /* The ring shows when the field's input would show its own: when it is focus-visible, which a
234
266
  text input is for a pointer as well as a keyboard. A focused action button draws its own. */
235
- [data-component~="ui-input-group"]:has(.field input:focus-visible) {
267
+ [data-component~="ui-input-group"]:has(.field :is(input, textarea):focus-visible) {
236
268
  border-color: var(--ui-control-focus, var(--ui-accent-solid));
237
269
  box-shadow:
238
270
  var(--ui-control-inset),
@@ -246,7 +278,7 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
246
278
  );
247
279
  }
248
280
 
249
- [data-component~="ui-input-group"]:has(input:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible) {
281
+ [data-component~="ui-input-group"]:has(:is(input, textarea):is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible) {
250
282
  box-shadow: var(
251
283
  --ui-input-group-invalid-focus-shadow,
252
284
  var(--ui-control-inset),
@@ -260,14 +292,14 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
260
292
 
261
293
  /* Forced colors drop box shadows, so the focus ring becomes an outline. */
262
294
  @media (forced-colors: active) {
263
- [data-component~="ui-input-group"]:has(input:focus-visible) {
295
+ [data-component~="ui-input-group"]:has(:is(input, textarea):focus-visible) {
264
296
  outline: var(--ui-focus-width) solid Highlight;
265
297
  outline-offset: var(--ui-border-width);
266
298
  }
267
299
  }
268
300
 
269
301
  [data-component~="ui-input-group"][data-ui-input-group-state~="disabled"],
270
- [data-component~="ui-input-group"]:has(input:disabled) {
302
+ [data-component~="ui-input-group"]:has(:is(input, textarea):disabled) {
271
303
  background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
272
304
  box-shadow: none;
273
305
  cursor: not-allowed;
@@ -323,6 +355,23 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
323
355
  padding-inline: var(--ui-space-1);
324
356
  }
325
357
 
358
+ /* Multiline text owns the first row; its action remains inside the same frame below it. */
359
+ [data-component~="ui-input-group"][data-ui-input-group-state~="multiline"] {
360
+ display: grid;
361
+ grid-template-columns: auto minmax(0, 1fr) auto;
362
+ }
363
+
364
+ [data-component~="ui-input-group"][data-ui-input-group-state~="multiline"] .field {
365
+ grid-column: 2;
366
+ }
367
+
368
+ [data-component~="ui-input-group"][data-ui-input-group-state~="multiline"] .action {
369
+ grid-column: 1 / -1;
370
+ justify-content: end;
371
+ padding: var(--ui-space-2);
372
+ padding-block-start: 0;
373
+ }
374
+
326
375
  .affix:empty,
327
376
  .action:empty {
328
377
  display: none;
@@ -282,6 +282,30 @@ useComponentHost(() => import('../components/ui-textarea/ui-textarea.js'), {
282
282
 
283
283
  <style scoped>
284
284
  /* This component's hooks style the element they are set on, and never a nested one. */
285
+ @property --ui-textarea-border-width {
286
+ syntax: "*";
287
+ inherits: false;
288
+ }
289
+ @property --ui-textarea-border {
290
+ syntax: "*";
291
+ inherits: false;
292
+ }
293
+ @property --ui-textarea-border-hover {
294
+ syntax: "*";
295
+ inherits: false;
296
+ }
297
+ @property --ui-textarea-focus-border {
298
+ syntax: "*";
299
+ inherits: false;
300
+ }
301
+ @property --ui-textarea-focus-outline {
302
+ syntax: "*";
303
+ inherits: false;
304
+ }
305
+ @property --ui-textarea-surface {
306
+ syntax: "*";
307
+ inherits: false;
308
+ }
285
309
  @property --ui-textarea-focus-shadow {
286
310
  syntax: "*";
287
311
  inherits: false;
@@ -314,13 +338,29 @@ useComponentHost(() => import('../components/ui-textarea/ui-textarea.js'), {
314
338
  padding: var(--ui-space-2) var(--ui-space-3);
315
339
  appearance: none;
316
340
  resize: vertical;
317
- border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
318
- border-radius: var(--ui-textarea-radius, var(--ui-field-radius, var(--ui-radius-md)));
319
- background: var(--ui-control-surface, var(--ui-surface-elevated));
341
+ border: var(
342
+ --ui-textarea-border-width,
343
+ var(--_ui-default-textarea-border-width, var(--ui-border-width))
344
+ ) solid
345
+ var(
346
+ --ui-textarea-border,
347
+ var(--_ui-default-textarea-border, var(--ui-control-border, var(--ui-border-strong)))
348
+ );
349
+ border-radius: var(
350
+ --ui-textarea-radius,
351
+ var(--_ui-default-textarea-radius, var(--ui-field-radius, var(--ui-radius-md)))
352
+ );
353
+ background: var(
354
+ --ui-textarea-surface,
355
+ var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated)))
356
+ );
320
357
  color: var(--ui-text-primary);
321
358
  font: inherit;
322
359
  line-height: var(--ui-line-height-md);
323
- box-shadow: var(--ui-textarea-shadow, var(--ui-control-inset));
360
+ box-shadow: var(
361
+ --ui-textarea-shadow,
362
+ var(--_ui-default-textarea-shadow, var(--ui-control-inset))
363
+ );
324
364
  transition:
325
365
  border-color var(--ui-motion-fast) var(--ui-motion-ease),
326
366
  box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
@@ -339,33 +379,54 @@ useComponentHost(() => import('../components/ui-textarea/ui-textarea.js'), {
339
379
  }
340
380
 
341
381
  [data-component~="ui-textarea"]:hover:not(:focus):not(:disabled):not(:read-only):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])) {
342
- border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
382
+ border-color: var(
383
+ --ui-textarea-border-hover,
384
+ var(
385
+ --_ui-default-textarea-border-hover,
386
+ var(--ui-control-border-hover, var(--ui-text-secondary))
387
+ )
388
+ );
343
389
  }
344
390
 
345
391
  [data-component~="ui-textarea"]:focus-visible {
346
- border-color: var(--ui-control-focus, var(--ui-accent-solid));
347
- outline: none;
392
+ border-color: var(
393
+ --ui-textarea-focus-border,
394
+ var(--_ui-default-textarea-focus-border, var(--ui-control-focus, var(--ui-accent-solid)))
395
+ );
396
+ outline: var(--ui-textarea-focus-outline, var(--_ui-default-textarea-focus-outline, none));
348
397
  box-shadow: var(
349
398
  --ui-textarea-focus-shadow,
350
- var(--ui-control-inset),
351
- var(--ui-control-focus-halo-shadow)
399
+ var(
400
+ --_ui-default-textarea-focus-shadow,
401
+ var(--ui-control-inset),
402
+ var(--ui-control-focus-halo-shadow)
403
+ )
352
404
  );
353
405
  }
354
406
 
355
407
  /* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
356
408
  [data-component~="ui-textarea"][aria-invalid="true"],
357
409
  [data-component~="ui-textarea"]:is(:user-invalid, [data-user-invalid]) {
358
- border-color: var(--ui-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)));
410
+ border-color: var(
411
+ --ui-textarea-invalid-border,
412
+ var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)))
413
+ );
359
414
  }
360
415
 
361
416
  [data-component~="ui-textarea"]:is([aria-invalid="true"], :is(:user-invalid, [data-user-invalid])):focus-visible {
362
417
  box-shadow: var(
363
418
  --ui-textarea-invalid-focus-shadow,
364
- var(--ui-control-inset),
365
- 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
366
- in srgb,
367
- var(--ui-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%,
368
- transparent
419
+ var(
420
+ --_ui-default-textarea-invalid-focus-shadow,
421
+ var(--ui-control-inset),
422
+ 0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
423
+ in srgb,
424
+ var(
425
+ --ui-textarea-invalid-border,
426
+ var(--_ui-default-textarea-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)))
427
+ ) 22%,
428
+ transparent
429
+ )
369
430
  )
370
431
  );
371
432
  }
@@ -5,6 +5,7 @@ import { computed, createElementBlock, createElementVNode, defineComponent, getC
5
5
  //#region .build/vue/UiInputGroup.vue?vue&type=script&setup=true&lang.ts
6
6
  var _hoisted_1 = [
7
7
  "data-disabled",
8
+ "data-multiline",
8
9
  "data-prefix-kind",
9
10
  "data-ui-input-group-state"
10
11
  ];
@@ -22,6 +23,10 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
22
23
  type: null,
23
24
  default: false
24
25
  },
26
+ multiline: {
27
+ type: null,
28
+ default: false
29
+ },
25
30
  prefixKind: {
26
31
  type: null,
27
32
  default: "text"
@@ -39,6 +44,16 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
39
44
  description: "Fades the group's framing to match a disabled input inside it. Disable the input itself too.",
40
45
  default: false
41
46
  },
47
+ multiline: {
48
+ type: {
49
+ kind: "terminal",
50
+ name: "boolean"
51
+ },
52
+ required: false,
53
+ target: { attribute: "multiline" },
54
+ description: "Groups a Textarea with an action below its text inside the shared frame.",
55
+ default: false
56
+ },
42
57
  prefixKind: {
43
58
  type: {
44
59
  kind: "terminal",
@@ -54,6 +69,7 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
54
69
  const props = __props;
55
70
  const acceptedProps = {
56
71
  disabled: false,
72
+ multiline: false,
57
73
  prefixKind: "text"
58
74
  };
59
75
  const checkedProps = computed(() => ({
@@ -61,6 +77,10 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
61
77
  kind: "terminal",
62
78
  name: "boolean"
63
79
  }, false, "disabled", acceptedProps),
80
+ multiline: checkedProp(props["multiline"], {
81
+ kind: "terminal",
82
+ name: "boolean"
83
+ }, false, "multiline", acceptedProps),
64
84
  prefixKind: checkedProp(props["prefixKind"], {
65
85
  kind: "terminal",
66
86
  name: "keyword"
@@ -96,7 +116,8 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
96
116
  const hostState = computed(() => [
97
117
  checkedProps.value.disabled && "disabled",
98
118
  checkedProps.value.prefixKind && "prefixKind",
99
- checkedProps.value.prefixKind != null && `prefixKind=${encodeURIComponent(checkedProps.value.prefixKind)}`
119
+ checkedProps.value.prefixKind != null && `prefixKind=${encodeURIComponent(checkedProps.value.prefixKind)}`,
120
+ checkedProps.value.multiline && "multiline"
100
121
  ].filter(Boolean).join(" "));
101
122
  const dispatch = createDispatch(root);
102
123
  useComponentHost(() => import("@threadlabs/looma/components/ui-input-group/ui-input-group.js"), {
@@ -112,7 +133,11 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
112
133
  const kebab = name.replace(/[A-Z]/g, (letter) => "-" + letter.toLowerCase());
113
134
  return incoming !== null && incoming !== void 0 ? incoming[name] ?? incoming[kebab] ?? null : null;
114
135
  },
115
- propNames: ["disabled", "prefixKind"],
136
+ propNames: [
137
+ "disabled",
138
+ "multiline",
139
+ "prefixKind"
140
+ ],
116
141
  propValidity: (name) => propValidityState({
117
142
  contract: propValidityContract,
118
143
  values: { ...props }
@@ -121,6 +146,7 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
121
146
  return (_ctx, _cache) => {
122
147
  return withDirectives((openBlock(), createElementBlock("div", mergeProps({ "data-component": "ui-input-group" }, nativeAttrs(_ctx.$attrs), {
123
148
  "data-disabled": reflectedProp("disabled", "disabled", checkedProps.value.disabled, void 0, false),
149
+ "data-multiline": reflectedProp("multiline", "multiline", checkedProps.value.multiline, void 0, false),
124
150
  "data-prefix-kind": reflectedProp("prefixKind", "prefix-kind", checkedProps.value.prefixKind, void 0, false),
125
151
  "data-ui-input-group-state": hostState.value || void 0,
126
152
  ref_key: "root",
@@ -136,6 +162,6 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
136
162
  }]]);
137
163
  };
138
164
  }
139
- }), [["__scopeId", "data-v-45a11c70"]]);
165
+ }), [["__scopeId", "data-v-875c0c72"]]);
140
166
  //#endregion
141
167
  export { UiInputGroup_default as t };
@@ -295,6 +295,6 @@ var UiTextarea_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
295
295
  }]]);
296
296
  };
297
297
  }
298
- }), [["__scopeId", "data-v-ce29cce2"]]);
298
+ }), [["__scopeId", "data-v-cb05f253"]]);
299
299
  //#endregion
300
300
  export { UiTextarea_default as t };