@threadlabs/looma 0.25.1 → 0.25.3

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.1",
3
+ "version": "0.25.3",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -189,6 +189,24 @@
189
189
  padding-block-start: 0;
190
190
  }
191
191
 
192
+ /* The same textarea keeps its full resizeable box. Its text reserves the action's live width,
193
+ so a translated label or pending indicator cannot cover what the person types. */
194
+ :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"] {
195
+ --_ui-default-textarea-padding-inline-end: calc(var(--_ui-input-group-action-inline-size, 7rem) + var(--ui-space-3));
196
+ }
197
+
198
+ :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"] .field {
199
+ grid-row: 1;
200
+ }
201
+
202
+ :scope[data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"] .action {
203
+ grid-column: 2;
204
+ grid-row: 1;
205
+ align-self: start;
206
+ justify-self: end;
207
+ padding: var(--ui-space-2);
208
+ }
209
+
192
210
  .affix:empty,
193
211
  .action:empty {
194
212
  display: none;
@@ -228,5 +246,8 @@
228
246
 
229
247
  /* Multiline text owns the first row; its action remains inside the same frame below it. */
230
248
 
249
+ /* The same textarea keeps its full resizeable box. Its text reserves the action's live width,
250
+ so a translated label or pending indicator cannot cover what the person types. */
251
+
231
252
  /* A component that sets its display still honours the hidden attribute on its root. */
232
253
  }
@@ -43,6 +43,10 @@
43
43
  syntax: "*";
44
44
  inherits: false;
45
45
  }
46
+ @property --ui-textarea-padding-inline-end {
47
+ syntax: "*";
48
+ inherits: false;
49
+ }
46
50
  @scope ([data-component~="ui-textarea"]) to ([data-component], [data-slotted]) {
47
51
  /* This component's hooks style the element they are set on, and never a nested one. */
48
52
 
@@ -55,6 +59,7 @@
55
59
  margin: 0;
56
60
  --_padding-block: var(--ui-space-2);
57
61
  padding: var(--ui-space-2) var(--ui-space-3);
62
+ padding-inline-end: var(--ui-textarea-padding-inline-end, var(--_ui-default-textarea-padding-inline-end, var(--ui-space-3)));
58
63
  appearance: none;
59
64
  resize: vertical;
60
65
  border: var(--ui-textarea-border-width, var(--_ui-default-textarea-border-width, var(--ui-border-width))) solid
@@ -123,12 +128,14 @@
123
128
  :scope[data-ui-textarea-state~="size=sm"] {
124
129
  --_padding-block: var(--ui-space-1);
125
130
  padding: var(--ui-space-1) var(--ui-space-2);
131
+ padding-inline-end: var(--ui-textarea-padding-inline-end, var(--_ui-default-textarea-padding-inline-end, var(--ui-space-2)));
126
132
  font-size: var(--ui-font-size-sm);
127
133
  }
128
134
 
129
135
  :scope[data-ui-textarea-state~="size=lg"] {
130
136
  --_padding-block: var(--ui-space-3);
131
137
  padding: var(--ui-space-3) var(--ui-space-4);
138
+ padding-inline-end: var(--ui-textarea-padding-inline-end, var(--_ui-default-textarea-padding-inline-end, var(--ui-space-4)));
132
139
  font-size: var(--ui-font-size-md);
133
140
  }
134
141
 
@@ -187,7 +187,7 @@
187
187
  transform: translateY(-20%) rotate(45deg);
188
188
  }
189
189
 
190
- :scope[aria-expanded="true"] .disclosure-icon {
190
+ :scope[aria-expanded="true"] > .row > .disclosure > .disclosure-icon {
191
191
  transform: rotate(0deg);
192
192
  }
193
193
 
@@ -6,6 +6,7 @@ export interface UiInputGroupElement extends HTMLDivElement {
6
6
  removeEventListener<K extends keyof UiInputGroupEventMap>(type: K, listener: (this: UiInputGroupElement, event: UiInputGroupEventMap[K]) => unknown, options?: boolean | EventListenerOptions): void;
7
7
  }
8
8
  export interface UiInputGroupProps {
9
+ actionPosition?: "below" | "top-end" | null;
9
10
  disabled?: boolean | null;
10
11
  multiline?: boolean | null;
11
12
  prefixKind?: "text" | "icon" | null;
@@ -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},"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:""};
6
+ const definition = {...{"contract":{"tag":"ui-input-group","props":{"actionPosition":{"type":{"kind":"terminal","name":"keyword"},"values":["below","top-end"],"required":false,"target":{"attribute":"actionposition"},"default":"below"},"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;
@@ -10,6 +10,10 @@ type __VLS_Slots = {} & {
10
10
  action?: (props: typeof __VLS_7) => any;
11
11
  };
12
12
  declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
13
+ actionPosition: {
14
+ type: PropType<"below" | "top-end" | null>;
15
+ default: string;
16
+ };
13
17
  disabled: {
14
18
  type: PropType<boolean | null>;
15
19
  default: boolean;
@@ -23,6 +27,10 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
23
27
  default: string;
24
28
  };
25
29
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
30
+ actionPosition: {
31
+ type: PropType<"below" | "top-end" | null>;
32
+ default: string;
33
+ };
26
34
  disabled: {
27
35
  type: PropType<boolean | null>;
28
36
  default: boolean;
@@ -38,6 +46,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
38
46
  }>> & Readonly<{}>, {
39
47
  disabled: boolean | null;
40
48
  multiline: boolean | null;
49
+ actionPosition: "top-end" | "below" | null;
41
50
  prefixKind: "text" | "icon" | null;
42
51
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
43
52
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -9,6 +9,15 @@ defineOptions({ inheritAttrs: false })
9
9
 
10
10
  const propValidityContract = {
11
11
  props: {
12
+ actionPosition: {
13
+ type: { kind: 'terminal', name: 'keyword' },
14
+ required: false,
15
+ target: { attribute: 'actionposition' },
16
+ description:
17
+ 'In a multiline group, below keeps the action under the text. top-end places it inside the textarea at its upper end and reserves text padding for its\n measured width.',
18
+ values: ['below', 'top-end'],
19
+ default: 'below',
20
+ },
12
21
  disabled: {
13
22
  type: { kind: 'terminal', name: 'boolean' },
14
23
  required: false,
@@ -36,16 +45,28 @@ const propValidityContract = {
36
45
  },
37
46
  } as const
38
47
  const props = defineProps({
48
+ actionPosition: {
49
+ type: null as unknown as PropType<'below' | 'top-end' | null>,
50
+ default: 'below',
51
+ },
39
52
  disabled: { type: null as unknown as PropType<boolean | null>, default: false },
40
53
  multiline: { type: null as unknown as PropType<boolean | null>, default: false },
41
54
  prefixKind: { type: null as unknown as PropType<'text' | 'icon' | null>, default: 'text' },
42
55
  })
43
56
  const acceptedProps: Record<string, unknown> = {
57
+ actionPosition: 'below',
44
58
  disabled: false,
45
59
  multiline: false,
46
60
  prefixKind: 'text',
47
61
  }
48
62
  const checkedProps = computed(() => ({
63
+ actionPosition: checkedProp<'below' | 'top-end'>(
64
+ props['actionPosition'],
65
+ { kind: 'terminal', name: 'keyword' },
66
+ false,
67
+ 'actionPosition',
68
+ acceptedProps,
69
+ ) as 'below' | 'top-end',
49
70
  disabled: checkedProp<boolean>(
50
71
  props['disabled'],
51
72
  { kind: 'terminal', name: 'boolean' },
@@ -132,6 +153,9 @@ const hostState = computed(() =>
132
153
  checkedProps.value.prefixKind != null
133
154
  && `prefixKind=${encodeURIComponent(checkedProps.value.prefixKind)}`,
134
155
  checkedProps.value.multiline && 'multiline',
156
+ checkedProps.value.actionPosition && 'actionPosition',
157
+ checkedProps.value.actionPosition != null
158
+ && `actionPosition=${encodeURIComponent(checkedProps.value.actionPosition)}`,
135
159
  ].filter(Boolean).join(' ')
136
160
  )
137
161
 
@@ -152,7 +176,7 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
152
176
  ? (incoming[name] ?? incoming[kebab] ?? null)
153
177
  : null
154
178
  },
155
- propNames: ['disabled', 'multiline', 'prefixKind'],
179
+ propNames: ['actionPosition', 'disabled', 'multiline', 'prefixKind'],
156
180
  propValidity: (name: string) =>
157
181
  propValidityState({ contract: propValidityContract, values: { ...props } }, name),
158
182
  })
@@ -163,6 +187,7 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
163
187
  data-component="ui-input-group"
164
188
  v-bind="nativeAttrs($attrs)"
165
189
  v-prop-validity="{ contract: propValidityContract, values: { ...props } }"
190
+ :data-action-position="reflectedProp('actionPosition', 'action-position', checkedProps.actionPosition, undefined, false)"
166
191
  :data-disabled="reflectedProp('disabled', 'disabled', checkedProps.disabled, undefined, false)"
167
192
  :data-multiline="reflectedProp('multiline', 'multiline', checkedProps.multiline, undefined, false)"
168
193
  :data-prefix-kind="reflectedProp('prefixKind', 'prefix-kind', checkedProps.prefixKind, undefined, false)"
@@ -372,6 +397,28 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
372
397
  padding-block-start: 0;
373
398
  }
374
399
 
400
+ /* The same textarea keeps its full resizeable box. Its text reserves the action's live width,
401
+ so a translated label or pending indicator cannot cover what the person types. */
402
+ [data-component~="ui-input-group"][data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"] {
403
+ --_ui-default-textarea-padding-inline-end: calc(
404
+ var(--_ui-input-group-action-inline-size, 7rem) + var(--ui-space-3)
405
+ );
406
+ }
407
+
408
+ [data-component~="ui-input-group"][data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]
409
+ .field {
410
+ grid-row: 1;
411
+ }
412
+
413
+ [data-component~="ui-input-group"][data-ui-input-group-state~="multiline"][data-ui-input-group-state~="actionPosition=top-end"]
414
+ .action {
415
+ grid-column: 2;
416
+ grid-row: 1;
417
+ align-self: start;
418
+ justify-self: end;
419
+ padding: var(--ui-space-2);
420
+ }
421
+
375
422
  .affix:empty,
376
423
  .action:empty {
377
424
  display: none;
@@ -326,6 +326,10 @@ useComponentHost(() => import('../components/ui-textarea/ui-textarea.js'), {
326
326
  syntax: "*";
327
327
  inherits: false;
328
328
  }
329
+ @property --ui-textarea-padding-inline-end {
330
+ syntax: "*";
331
+ inherits: false;
332
+ }
329
333
 
330
334
  [data-component~="ui-textarea"] {
331
335
  box-sizing: border-box;
@@ -336,6 +340,10 @@ useComponentHost(() => import('../components/ui-textarea/ui-textarea.js'), {
336
340
  margin: 0;
337
341
  --_padding-block: var(--ui-space-2);
338
342
  padding: var(--ui-space-2) var(--ui-space-3);
343
+ padding-inline-end: var(
344
+ --ui-textarea-padding-inline-end,
345
+ var(--_ui-default-textarea-padding-inline-end, var(--ui-space-3))
346
+ );
339
347
  appearance: none;
340
348
  resize: vertical;
341
349
  border: var(
@@ -441,12 +449,20 @@ useComponentHost(() => import('../components/ui-textarea/ui-textarea.js'), {
441
449
  [data-component~="ui-textarea"][data-ui-textarea-state~="size=sm"] {
442
450
  --_padding-block: var(--ui-space-1);
443
451
  padding: var(--ui-space-1) var(--ui-space-2);
452
+ padding-inline-end: var(
453
+ --ui-textarea-padding-inline-end,
454
+ var(--_ui-default-textarea-padding-inline-end, var(--ui-space-2))
455
+ );
444
456
  font-size: var(--ui-font-size-sm);
445
457
  }
446
458
 
447
459
  [data-component~="ui-textarea"][data-ui-textarea-state~="size=lg"] {
448
460
  --_padding-block: var(--ui-space-3);
449
461
  padding: var(--ui-space-3) var(--ui-space-4);
462
+ padding-inline-end: var(
463
+ --ui-textarea-padding-inline-end,
464
+ var(--_ui-default-textarea-padding-inline-end, var(--ui-space-4))
465
+ );
450
466
  font-size: var(--ui-font-size-md);
451
467
  }
452
468
 
@@ -878,7 +878,7 @@ function concat<T extends unknown[]>(...values: T): ConcatResult<T> {
878
878
  transform: translateY(-20%) rotate(45deg);
879
879
  }
880
880
 
881
- [data-component~="ui-tree-item"][aria-expanded="true"] .disclosure-icon {
881
+ [data-component~="ui-tree-item"][aria-expanded="true"] > .row > .disclosure > .disclosure-icon {
882
882
  transform: rotate(0deg);
883
883
  }
884
884
 
@@ -4,6 +4,7 @@ import { a as vPropValidity, n as checkedProp, r as propValidityState } from "./
4
4
  import { computed, createElementBlock, createElementVNode, defineComponent, getCurrentInstance, mergeProps, openBlock, ref, renderSlot, unref, watchSyncEffect, withDirectives } from "vue";
5
5
  //#region .build/vue/UiInputGroup.vue?vue&type=script&setup=true&lang.ts
6
6
  var _hoisted_1 = [
7
+ "data-action-position",
7
8
  "data-disabled",
8
9
  "data-multiline",
9
10
  "data-prefix-kind",
@@ -19,6 +20,10 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
19
20
  inheritAttrs: false,
20
21
  __name: "UiInputGroup",
21
22
  props: {
23
+ actionPosition: {
24
+ type: null,
25
+ default: "below"
26
+ },
22
27
  disabled: {
23
28
  type: null,
24
29
  default: false
@@ -34,6 +39,17 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
34
39
  },
35
40
  setup(__props) {
36
41
  const propValidityContract = { props: {
42
+ actionPosition: {
43
+ type: {
44
+ kind: "terminal",
45
+ name: "keyword"
46
+ },
47
+ required: false,
48
+ target: { attribute: "actionposition" },
49
+ description: "In a multiline group, below keeps the action under the text. top-end places it inside the textarea at its upper end and reserves text padding for its\n measured width.",
50
+ values: ["below", "top-end"],
51
+ default: "below"
52
+ },
37
53
  disabled: {
38
54
  type: {
39
55
  kind: "terminal",
@@ -68,11 +84,16 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
68
84
  } };
69
85
  const props = __props;
70
86
  const acceptedProps = {
87
+ actionPosition: "below",
71
88
  disabled: false,
72
89
  multiline: false,
73
90
  prefixKind: "text"
74
91
  };
75
92
  const checkedProps = computed(() => ({
93
+ actionPosition: checkedProp(props["actionPosition"], {
94
+ kind: "terminal",
95
+ name: "keyword"
96
+ }, false, "actionPosition", acceptedProps),
76
97
  disabled: checkedProp(props["disabled"], {
77
98
  kind: "terminal",
78
99
  name: "boolean"
@@ -117,7 +138,9 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
117
138
  checkedProps.value.disabled && "disabled",
118
139
  checkedProps.value.prefixKind && "prefixKind",
119
140
  checkedProps.value.prefixKind != null && `prefixKind=${encodeURIComponent(checkedProps.value.prefixKind)}`,
120
- checkedProps.value.multiline && "multiline"
141
+ checkedProps.value.multiline && "multiline",
142
+ checkedProps.value.actionPosition && "actionPosition",
143
+ checkedProps.value.actionPosition != null && `actionPosition=${encodeURIComponent(checkedProps.value.actionPosition)}`
121
144
  ].filter(Boolean).join(" "));
122
145
  const dispatch = createDispatch(root);
123
146
  useComponentHost(() => import("@threadlabs/looma/components/ui-input-group/ui-input-group.js"), {
@@ -134,6 +157,7 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
134
157
  return incoming !== null && incoming !== void 0 ? incoming[name] ?? incoming[kebab] ?? null : null;
135
158
  },
136
159
  propNames: [
160
+ "actionPosition",
137
161
  "disabled",
138
162
  "multiline",
139
163
  "prefixKind"
@@ -145,6 +169,7 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
145
169
  });
146
170
  return (_ctx, _cache) => {
147
171
  return withDirectives((openBlock(), createElementBlock("div", mergeProps({ "data-component": "ui-input-group" }, nativeAttrs(_ctx.$attrs), {
172
+ "data-action-position": reflectedProp("actionPosition", "action-position", checkedProps.value.actionPosition, void 0, false),
148
173
  "data-disabled": reflectedProp("disabled", "disabled", checkedProps.value.disabled, void 0, false),
149
174
  "data-multiline": reflectedProp("multiline", "multiline", checkedProps.value.multiline, void 0, false),
150
175
  "data-prefix-kind": reflectedProp("prefixKind", "prefix-kind", checkedProps.value.prefixKind, void 0, false),
@@ -162,6 +187,6 @@ var UiInputGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
162
187
  }]]);
163
188
  };
164
189
  }
165
- }), [["__scopeId", "data-v-875c0c72"]]);
190
+ }), [["__scopeId", "data-v-57e02424"]]);
166
191
  //#endregion
167
192
  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-cb05f253"]]);
298
+ }), [["__scopeId", "data-v-f5b6ae17"]]);
299
299
  //#endregion
300
300
  export { UiTextarea_default as t };
@@ -676,6 +676,6 @@ var UiTreeItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
676
676
  }]]);
677
677
  };
678
678
  }
679
- }), [["__scopeId", "data-v-4981f1b6"]]);
679
+ }), [["__scopeId", "data-v-f2ec0a81"]]);
680
680
  //#endregion
681
681
  export { UiTreeItem_default as t };