@threadlabs/looma 0.15.1 → 0.15.2
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-form-field/ui-form-field.html +23 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-form-field.css +23 -0
- package/tokens.css +12 -0
- package/vanilla/UiFormField.js +20 -9
- package/vue/UiFormField.d.ts +6 -4
- package/vue/UiFormField.vue +23 -1
- package/vue/chunks/UiFormField.js +7 -6
- package/vue/components.css +28 -11
package/package.json
CHANGED
package/styles/ui-form-field.css
CHANGED
|
@@ -35,6 +35,25 @@
|
|
|
35
35
|
font-size: var(--ui-font-size-sm);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/* A label action, such as a small help button, sits after the label text in a box one line
|
|
39
|
+
tall, centred on the first line and overflowing it. A button taller than a line of label
|
|
40
|
+
text then never makes this field's label row taller than its neighbours' in a grid row. */
|
|
41
|
+
.label:has(> .label-action > *) {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: start;
|
|
44
|
+
gap: var(--ui-space-1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.label-action {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: unsafe center;
|
|
50
|
+
block-size: 1lh;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.label-action:not(:has(> *)) {
|
|
54
|
+
display: none;
|
|
55
|
+
}
|
|
56
|
+
|
|
38
57
|
.help {
|
|
39
58
|
color: var(--ui-text-muted);
|
|
40
59
|
font-size: var(--ui-font-size-sm);
|
|
@@ -62,6 +81,10 @@
|
|
|
62
81
|
/* Each named region styles whatever fills it, however it was slotted (slot="…" in HTML, #label
|
|
63
82
|
in Vue), and generates no box of its own, so an empty region adds no gap. */
|
|
64
83
|
|
|
84
|
+
/* A label action, such as a small help button, sits after the label text in a box one line
|
|
85
|
+
tall, centred on the first line and overflowing it. A button taller than a line of label
|
|
86
|
+
text then never makes this field's label row taller than its neighbours' in a grid row. */
|
|
87
|
+
|
|
65
88
|
.help > :where([data-slotted], [data-slotted] *):is(*) {
|
|
66
89
|
min-block-size: var(--_ui-form-field-help-min-block-size, 0);
|
|
67
90
|
}
|
package/tokens.css
CHANGED
|
@@ -271,3 +271,15 @@
|
|
|
271
271
|
from { --_ui-scroll-fade-end: var(--_ui-scroll-fade-size, 24px); }
|
|
272
272
|
to { --_ui-scroll-fade-end: 0px; }
|
|
273
273
|
}
|
|
274
|
+
|
|
275
|
+
/* Focus moved by script to a non-interactive target, such as a page's <h1 tabindex="-1"> after
|
|
276
|
+
navigation so a screen reader announces the new page, draws no focus ring: tabindex="-1" keeps
|
|
277
|
+
the target out of the Tab order, so there is no keyboard focus position to show, and the ring
|
|
278
|
+
around a wrapped heading is a jagged, notched box. Controls, links, editable regions, and any
|
|
279
|
+
element with a role (menu items, options, tabs, and other roving-focus widgets) keep their ring.
|
|
280
|
+
Layered, so any page or component focus style still wins. */
|
|
281
|
+
@layer base {
|
|
282
|
+
[tabindex="-1"]:not(a[href], area[href], audio, button, iframe, input, select, summary, textarea, video, [contenteditable], [role]):focus-visible {
|
|
283
|
+
outline: none;
|
|
284
|
+
}
|
|
285
|
+
}
|
package/vanilla/UiFormField.js
CHANGED
|
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
|
|
|
3
3
|
import * as controller from "../components/ui-form-field/ui-form-field.js";
|
|
4
4
|
import "../styles/ui-form-field.css";
|
|
5
5
|
|
|
6
|
-
const definition = {...{"contract":{"tag":"ui-form-field","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"invalid":{"type":"boolean","required":false,"target":{"attribute":"invalid"},"default":false},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false}}},"template":{"kind":"element","name":"div","attributes":[],"children":[{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot","fallback":[],"name":"label"}]},{"kind":"slot"},{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"class","value":"help"}],"children":[{"kind":"slot","fallback":[],"name":"help"}]},{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"class","value":"error"}],"children":[{"kind":"slot","fallback":[],"name":"error"}]}]},"declarations":[],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
|
|
6
|
+
const definition = {...{"contract":{"tag":"ui-form-field","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"invalid":{"type":"boolean","required":false,"target":{"attribute":"invalid"},"default":false},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false}}},"template":{"kind":"element","name":"div","attributes":[],"children":[{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"class","value":"label"}],"children":[{"kind":"slot","fallback":[],"name":"label"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"label-action"}],"children":[{"kind":"slot","fallback":[],"name":"label-action"}]}]},{"kind":"slot"},{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"class","value":"help"}],"children":[{"kind":"slot","fallback":[],"name":"help"}]},{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"class","value":"error"}],"children":[{"kind":"slot","fallback":[],"name":"error"}]}]},"declarations":[],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
|
|
7
7
|
|
|
8
8
|
export function createUiFormField(options = {}) {
|
|
9
9
|
const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
|
|
@@ -24,6 +24,17 @@ export function createUiFormField(options = {}) {
|
|
|
24
24
|
}
|
|
25
25
|
} else {
|
|
26
26
|
}
|
|
27
|
+
const element1 = document.createElement("span");
|
|
28
|
+
element1.setAttribute("class", "label-action");
|
|
29
|
+
if (slots["label-action"] ?? [].length > 0) {
|
|
30
|
+
for (const child of slots["label-action"] ?? []) {
|
|
31
|
+
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
32
|
+
projected.push([node, "label-action"]);
|
|
33
|
+
element1.append(node);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
}
|
|
37
|
+
element0.append(element1);
|
|
27
38
|
element.append(element0);
|
|
28
39
|
if (children.length > 0) {
|
|
29
40
|
for (const child of children) {
|
|
@@ -33,28 +44,28 @@ export function createUiFormField(options = {}) {
|
|
|
33
44
|
}
|
|
34
45
|
} else {
|
|
35
46
|
}
|
|
36
|
-
const
|
|
37
|
-
|
|
47
|
+
const element2 = document.createElement("div");
|
|
48
|
+
element2.setAttribute("class", "help");
|
|
38
49
|
if (slots["help"] ?? [].length > 0) {
|
|
39
50
|
for (const child of slots["help"] ?? []) {
|
|
40
51
|
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
41
52
|
projected.push([node, "help"]);
|
|
42
|
-
|
|
53
|
+
element2.append(node);
|
|
43
54
|
}
|
|
44
55
|
} else {
|
|
45
56
|
}
|
|
46
|
-
element.append(
|
|
47
|
-
const
|
|
48
|
-
|
|
57
|
+
element.append(element2);
|
|
58
|
+
const element3 = document.createElement("div");
|
|
59
|
+
element3.setAttribute("class", "error");
|
|
49
60
|
if (slots["error"] ?? [].length > 0) {
|
|
50
61
|
for (const child of slots["error"] ?? []) {
|
|
51
62
|
const node = typeof child === "string" ? document.createTextNode(child) : child;
|
|
52
63
|
projected.push([node, "error"]);
|
|
53
|
-
|
|
64
|
+
element3.append(node);
|
|
54
65
|
}
|
|
55
66
|
} else {
|
|
56
67
|
}
|
|
57
|
-
element.append(
|
|
68
|
+
element.append(element3);
|
|
58
69
|
manageComponentLifecycle(element, definition, { props: componentProps, projected,
|
|
59
70
|
controller,
|
|
60
71
|
});
|
package/vue/UiFormField.d.ts
CHANGED
|
@@ -3,15 +3,17 @@ type __VLS_Props = {
|
|
|
3
3
|
invalid?: boolean;
|
|
4
4
|
required?: boolean;
|
|
5
5
|
};
|
|
6
|
-
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {};
|
|
6
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {}, __VLS_9: {};
|
|
7
7
|
type __VLS_Slots = {} & {
|
|
8
8
|
label?: (props: typeof __VLS_1) => any;
|
|
9
9
|
} & {
|
|
10
|
-
|
|
10
|
+
'label-action'?: (props: typeof __VLS_3) => any;
|
|
11
11
|
} & {
|
|
12
|
-
|
|
12
|
+
default?: (props: typeof __VLS_5) => any;
|
|
13
13
|
} & {
|
|
14
|
-
|
|
14
|
+
help?: (props: typeof __VLS_7) => any;
|
|
15
|
+
} & {
|
|
16
|
+
error?: (props: typeof __VLS_9) => any;
|
|
15
17
|
};
|
|
16
18
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
17
19
|
invalid: boolean;
|
package/vue/UiFormField.vue
CHANGED
|
@@ -44,7 +44,10 @@ useComponentHost(controllerModule.default, {
|
|
|
44
44
|
:data-ui-form-field-state="hostState || undefined"
|
|
45
45
|
ref="root"
|
|
46
46
|
>
|
|
47
|
-
<div class="label"
|
|
47
|
+
<div class="label">
|
|
48
|
+
<slot name="label" />
|
|
49
|
+
<span class="label-action"><slot name="label-action" /></span>
|
|
50
|
+
</div>
|
|
48
51
|
<slot />
|
|
49
52
|
<div class="help"><slot name="help" /></div>
|
|
50
53
|
<div class="error"><slot name="error" /></div>
|
|
@@ -87,6 +90,25 @@ useComponentHost(controllerModule.default, {
|
|
|
87
90
|
font-size: var(--ui-font-size-sm);
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
/* A label action, such as a small help button, sits after the label text in a box one line
|
|
94
|
+
tall, centred on the first line and overflowing it. A button taller than a line of label
|
|
95
|
+
text then never makes this field's label row taller than its neighbours' in a grid row. */
|
|
96
|
+
.label:has(> .label-action > *) {
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: start;
|
|
99
|
+
gap: var(--ui-space-1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.label-action {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: unsafe center;
|
|
105
|
+
block-size: 1lh;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.label-action:not(:has(> *)) {
|
|
109
|
+
display: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
90
112
|
.help {
|
|
91
113
|
color: var(--ui-text-muted);
|
|
92
114
|
font-size: var(--ui-font-size-sm);
|
|
@@ -5,8 +5,9 @@ import * as controllerModule from "@threadlabs/looma/components/ui-form-field/ui
|
|
|
5
5
|
//#region .build/vue/UiFormField.vue?vue&type=script&setup=true&lang.ts
|
|
6
6
|
var _hoisted_1 = ["data-ui-form-field-state"];
|
|
7
7
|
var _hoisted_2 = { class: "label" };
|
|
8
|
-
var _hoisted_3 = { class: "
|
|
9
|
-
var _hoisted_4 = { class: "
|
|
8
|
+
var _hoisted_3 = { class: "label-action" };
|
|
9
|
+
var _hoisted_4 = { class: "help" };
|
|
10
|
+
var _hoisted_5 = { class: "error" };
|
|
10
11
|
//#endregion
|
|
11
12
|
//#region .build/vue/UiFormField.vue
|
|
12
13
|
var UiFormField_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
@@ -43,13 +44,13 @@ var UiFormField_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
|
|
|
43
44
|
ref_key: "root",
|
|
44
45
|
ref: root
|
|
45
46
|
}), [
|
|
46
|
-
createElementVNode("div", _hoisted_2, [renderSlot(_ctx.$slots, "label")]),
|
|
47
|
+
createElementVNode("div", _hoisted_2, [renderSlot(_ctx.$slots, "label"), createElementVNode("span", _hoisted_3, [renderSlot(_ctx.$slots, "label-action")])]),
|
|
47
48
|
renderSlot(_ctx.$slots, "default"),
|
|
48
|
-
createElementVNode("div",
|
|
49
|
-
createElementVNode("div",
|
|
49
|
+
createElementVNode("div", _hoisted_4, [renderSlot(_ctx.$slots, "help")]),
|
|
50
|
+
createElementVNode("div", _hoisted_5, [renderSlot(_ctx.$slots, "error")])
|
|
50
51
|
], 16, _hoisted_1);
|
|
51
52
|
};
|
|
52
53
|
}
|
|
53
|
-
}), [["__scopeId", "data-v-
|
|
54
|
+
}), [["__scopeId", "data-v-453329c8"]]);
|
|
54
55
|
//#endregion
|
|
55
56
|
export { UiFormField_default as t };
|
package/vue/components.css
CHANGED
|
@@ -3878,7 +3878,7 @@ svg[data-v-70304a1a-s] {
|
|
|
3878
3878
|
syntax: "*";
|
|
3879
3879
|
inherits: false;
|
|
3880
3880
|
}
|
|
3881
|
-
[data-component~="ui-form-field"][data-v-
|
|
3881
|
+
[data-component~="ui-form-field"][data-v-453329c8] {
|
|
3882
3882
|
/* Hooks do not inherit, so the parts below read them from the root. */
|
|
3883
3883
|
--_ui-form-field-danger: var(--ui-form-field-danger);
|
|
3884
3884
|
--_ui-form-field-help-min-block-size: var(--ui-form-field-help-min-block-size);
|
|
@@ -3892,38 +3892,55 @@ svg[data-v-70304a1a-s] {
|
|
|
3892
3892
|
|
|
3893
3893
|
/* Each named region styles whatever fills it, however it was slotted (slot="…" in HTML, #label
|
|
3894
3894
|
in Vue), and generates no box of its own, so an empty region adds no gap. */
|
|
3895
|
-
.label[data-v-
|
|
3896
|
-
.help[data-v-
|
|
3897
|
-
.error[data-v-
|
|
3895
|
+
.label[data-v-453329c8],
|
|
3896
|
+
.help[data-v-453329c8],
|
|
3897
|
+
.error[data-v-453329c8] {
|
|
3898
3898
|
display: contents;
|
|
3899
3899
|
}
|
|
3900
|
-
.label[data-v-
|
|
3900
|
+
.label[data-v-453329c8] {
|
|
3901
3901
|
color: var(--ui-text-secondary);
|
|
3902
3902
|
font-size: var(--ui-font-size-sm);
|
|
3903
3903
|
}
|
|
3904
|
-
|
|
3904
|
+
|
|
3905
|
+
/* A label action, such as a small help button, sits after the label text in a box one line
|
|
3906
|
+
tall, centred on the first line and overflowing it. A button taller than a line of label
|
|
3907
|
+
text then never makes this field's label row taller than its neighbours' in a grid row. */
|
|
3908
|
+
.label[data-v-453329c8]:has(> .label-action > *) {
|
|
3909
|
+
display: flex;
|
|
3910
|
+
align-items: start;
|
|
3911
|
+
gap: var(--ui-space-1);
|
|
3912
|
+
}
|
|
3913
|
+
.label-action[data-v-453329c8] {
|
|
3914
|
+
display: flex;
|
|
3915
|
+
align-items: unsafe center;
|
|
3916
|
+
block-size: 1lh;
|
|
3917
|
+
}
|
|
3918
|
+
.label-action[data-v-453329c8]:not(:has(> *)) {
|
|
3919
|
+
display: none;
|
|
3920
|
+
}
|
|
3921
|
+
.help[data-v-453329c8] {
|
|
3905
3922
|
color: var(--ui-text-muted);
|
|
3906
3923
|
font-size: var(--ui-font-size-sm);
|
|
3907
3924
|
line-height: var(--ui-line-height-md);
|
|
3908
3925
|
}
|
|
3909
|
-
.help > [data-v-
|
|
3926
|
+
.help > [data-v-453329c8-s] {
|
|
3910
3927
|
min-block-size: var(--_ui-form-field-help-min-block-size, 0);
|
|
3911
3928
|
}
|
|
3912
|
-
.error[data-v-
|
|
3929
|
+
.error[data-v-453329c8] {
|
|
3913
3930
|
display: none;
|
|
3914
3931
|
}
|
|
3915
|
-
[data-component~="ui-form-field"][data-ui-form-field-state~="invalid"] .error[data-v-
|
|
3932
|
+
[data-component~="ui-form-field"][data-ui-form-field-state~="invalid"] .error[data-v-453329c8] {
|
|
3916
3933
|
display: contents;
|
|
3917
3934
|
}
|
|
3918
3935
|
|
|
3919
3936
|
/* An error reads in the danger colour, the one the invalid control's border takes. */
|
|
3920
|
-
.error[data-v-
|
|
3937
|
+
.error[data-v-453329c8],[role="alert"][data-v-453329c8-s] {
|
|
3921
3938
|
color: var(--_ui-form-field-danger, var(--ui-field-danger, var(--ui-danger)));
|
|
3922
3939
|
font-size: var(--ui-font-size-sm);
|
|
3923
3940
|
}
|
|
3924
3941
|
|
|
3925
3942
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
3926
|
-
[data-component~="ui-form-field"][hidden][data-v-
|
|
3943
|
+
[data-component~="ui-form-field"][hidden][data-v-453329c8] {
|
|
3927
3944
|
display: none;
|
|
3928
3945
|
}
|
|
3929
3946
|
|