@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/components/ui-input-group/ui-input-group.html +34 -5
- package/components/ui-input-group/ui-input-group.js +2 -2
- package/components/ui-textarea/ui-textarea.html +42 -14
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-input-group.css +35 -5
- package/styles/ui-textarea.css +42 -14
- package/vanilla/UiInputGroup.d.ts +1 -0
- package/vanilla/UiInputGroup.js +1 -1
- package/vue/UiInputGroup.d.ts +9 -0
- package/vue/UiInputGroup.vue +56 -7
- package/vue/UiTextarea.vue +76 -15
- package/vue/chunks/UiInputGroup.js +29 -3
- package/vue/chunks/UiTextarea.js +1 -1
- package/vue/components.css +139 -52
package/package.json
CHANGED
|
@@ -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
|
}
|
package/styles/ui-textarea.css
CHANGED
|
@@ -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
|
|
37
|
-
|
|
38
|
-
|
|
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(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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)[];
|
package/vanilla/UiInputGroup.js
CHANGED
|
@@ -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;
|
package/vue/UiInputGroup.d.ts
CHANGED
|
@@ -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>;
|
package/vue/UiInputGroup.vue
CHANGED
|
@@ -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> = {
|
|
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;
|
package/vue/UiTextarea.vue
CHANGED
|
@@ -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(
|
|
318
|
-
|
|
319
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
347
|
-
|
|
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(
|
|
351
|
-
|
|
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(
|
|
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(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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: [
|
|
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-
|
|
165
|
+
}), [["__scopeId", "data-v-875c0c72"]]);
|
|
140
166
|
//#endregion
|
|
141
167
|
export { UiInputGroup_default as t };
|
package/vue/chunks/UiTextarea.js
CHANGED