@dumiflex/wp-kit 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/Input.vue +10 -1
- package/src/Popover.vue +31 -7
- package/src/Textarea.vue +8 -0
- package/src/floating.ts +12 -0
- package/src/input.css +138 -0
- package/src/theme/tokens.css +10 -94
- package/src/tokens.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.12.0](https://github.com/DumiFlex/wp-kit/compare/v0.11.0...v0.12.0) (2026-11-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Input and Textarea take mono and an accent / warn tone, and size themselves border-box ([#98](https://github.com/DumiFlex/wp-kit/issues/98)) ([7b1023f](https://github.com/DumiFlex/wp-kit/commit/7b1023f699e7f9b37904abf0200ed805f1db23a7))
|
|
9
|
+
|
|
10
|
+
## [0.11.0](https://github.com/DumiFlex/wp-kit/compare/v0.10.0...v0.11.0) (2026-11-03)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* Popover takes a virtual anchor and passes attrs to its panel; Input and Textarea style themselves on the canvas ([#94](https://github.com/DumiFlex/wp-kit/issues/94)) ([d5e17ef](https://github.com/DumiFlex/wp-kit/commit/d5e17eff909e259d1ed8dbfa3b0fbc2060e9e213))
|
|
16
|
+
|
|
3
17
|
## [0.10.0](https://github.com/DumiFlex/wp-kit/compare/v0.9.0...v0.10.0) (2026-11-03)
|
|
4
18
|
|
|
5
19
|
|
package/package.json
CHANGED
package/src/Input.vue
CHANGED
|
@@ -24,6 +24,10 @@ interface Props {
|
|
|
24
24
|
suffix?: string | undefined;
|
|
25
25
|
id?: string | undefined;
|
|
26
26
|
ariaLabel?: string | undefined;
|
|
27
|
+
/** Mono text: variable names, ids, tags. */
|
|
28
|
+
mono?: boolean | undefined;
|
|
29
|
+
/** "accent" marks a value changed from its default, "warn" one that needs a look. */
|
|
30
|
+
tone?: "accent" | "warn" | undefined;
|
|
27
31
|
}
|
|
28
32
|
const props = withDefaults(defineProps<Props>(), {
|
|
29
33
|
type: "text",
|
|
@@ -57,7 +61,7 @@ function onInput(e: Event) {
|
|
|
57
61
|
</script>
|
|
58
62
|
|
|
59
63
|
<template>
|
|
60
|
-
<div v-if="wrapped" :class="groupClasses" :aria-invalid="error || undefined">
|
|
64
|
+
<div v-if="wrapped" :class="groupClasses" :aria-invalid="error || undefined" :data-tone="tone">
|
|
61
65
|
<span v-if="icon" class="wp-input-group__icon" aria-hidden="true">
|
|
62
66
|
<Icon :name="icon" />
|
|
63
67
|
</span>
|
|
@@ -65,6 +69,7 @@ function onInput(e: Event) {
|
|
|
65
69
|
<input
|
|
66
70
|
:id="id"
|
|
67
71
|
:class="inputClasses"
|
|
72
|
+
:data-mono="mono || undefined"
|
|
68
73
|
:type="type"
|
|
69
74
|
:value="modelValue"
|
|
70
75
|
:placeholder="placeholder"
|
|
@@ -85,6 +90,8 @@ function onInput(e: Event) {
|
|
|
85
90
|
v-else
|
|
86
91
|
:id="id"
|
|
87
92
|
:class="inputClasses"
|
|
93
|
+
:data-mono="mono || undefined"
|
|
94
|
+
:data-tone="tone"
|
|
88
95
|
:type="type"
|
|
89
96
|
:value="modelValue"
|
|
90
97
|
:placeholder="placeholder"
|
|
@@ -97,3 +104,5 @@ function onInput(e: Event) {
|
|
|
97
104
|
@focus="(e) => emit('focus', e)"
|
|
98
105
|
/>
|
|
99
106
|
</template>
|
|
107
|
+
|
|
108
|
+
<style scoped src="./input.css"></style>
|
package/src/Popover.vue
CHANGED
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
*
|
|
18
18
|
* <Popover :open="!!cell" :anchor="cell?.el" label="Cell rule" @update:open="cell = null">…</Popover>
|
|
19
19
|
*
|
|
20
|
+
* A list that follows the text caret, or a card opened at a point, passes a
|
|
21
|
+
* virtual anchor instead: anything with `getBoundingClientRect()` (see
|
|
22
|
+
* `VirtualAnchor` in floating.ts). Hand it a new object to move the panel.
|
|
23
|
+
*
|
|
24
|
+
* <Popover :open="!!at" :anchor="at" :autofocus="false" role="listbox" @update:open="at = null">…</Popover>
|
|
25
|
+
*
|
|
26
|
+
* Attributes (`class`, `data-test`, `@mouseenter` …) go on the panel, so a
|
|
27
|
+
* hover card can keep itself open while the pointer is over it.
|
|
28
|
+
*
|
|
20
29
|
* `variant="menu"` draws the panel as a menu (`menu.css`, the look Select's
|
|
21
30
|
* list, ContextMenu and Combobox share): tighter padding, 4px from the
|
|
22
31
|
* trigger, above modals, and it closes once its trigger scrolls out of view.
|
|
@@ -24,9 +33,11 @@
|
|
|
24
33
|
* scrolls past that instead of covering its trigger.
|
|
25
34
|
*/
|
|
26
35
|
import { computed, nextTick, onBeforeUnmount, ref, useId, watch } from "vue";
|
|
27
|
-
import { placeFloating, roomOn, viewportSize, type Placement, type Side } from "./floating";
|
|
36
|
+
import { placeFloating, roomOn, viewportSize, type Placement, type Side, type VirtualAnchor } from "./floating";
|
|
28
37
|
import { provideFloatingLayer, useFloatingChild } from "./floating-layer";
|
|
29
38
|
|
|
39
|
+
defineOptions({ inheritAttrs: false });
|
|
40
|
+
|
|
30
41
|
const props = withDefaults(defineProps<{
|
|
31
42
|
open: boolean;
|
|
32
43
|
placement?: Placement | undefined;
|
|
@@ -38,8 +49,9 @@ const props = withDefaults(defineProps<{
|
|
|
38
49
|
autofocus?: boolean | undefined;
|
|
39
50
|
/** "dialog" for panels with controls, "menu"/"listbox" when the content is one. */
|
|
40
51
|
role?: "dialog" | "menu" | "listbox" | "group" | undefined;
|
|
41
|
-
/** Place the panel against this element
|
|
42
|
-
|
|
52
|
+
/** Place the panel against this element (or a virtual anchor: a caret, a
|
|
53
|
+
* point) instead of the trigger slot. */
|
|
54
|
+
anchor?: HTMLElement | VirtualAnchor | null | undefined;
|
|
43
55
|
/** "menu" for a list of choices (Select, Combobox): the shared menu look. */
|
|
44
56
|
variant?: "panel" | "menu" | undefined;
|
|
45
57
|
/** Tallest the panel grows, in px, before it scrolls. */
|
|
@@ -59,12 +71,19 @@ const MIN_HEIGHT = 80;
|
|
|
59
71
|
const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), '
|
|
60
72
|
+ 'textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
61
73
|
|
|
62
|
-
|
|
74
|
+
/** What the panel is placed against: the anchor prop or the trigger slot. */
|
|
75
|
+
function trigger(): HTMLElement | VirtualAnchor | null {
|
|
63
76
|
if (props.anchor) return props.anchor;
|
|
64
77
|
const el = anchorEl.value;
|
|
65
78
|
return (el?.firstElementChild as HTMLElement | null) ?? el;
|
|
66
79
|
}
|
|
67
80
|
|
|
81
|
+
/** The anchor as an element, when it is one (a virtual anchor takes no focus). */
|
|
82
|
+
function anchorElement(): HTMLElement | null {
|
|
83
|
+
const a = props.anchor;
|
|
84
|
+
return a instanceof HTMLElement ? a : null;
|
|
85
|
+
}
|
|
86
|
+
|
|
68
87
|
function setOpen(v: boolean): void {
|
|
69
88
|
if (v !== props.open) emit("update:open", v);
|
|
70
89
|
}
|
|
@@ -105,7 +124,9 @@ function triggerGone(): boolean {
|
|
|
105
124
|
if (!t) return false;
|
|
106
125
|
const r = t.getBoundingClientRect();
|
|
107
126
|
const view = viewportSize();
|
|
108
|
-
|
|
127
|
+
const bottom = r.top + r.height;
|
|
128
|
+
const right = r.left + r.width;
|
|
129
|
+
return bottom < 0 || r.top > view.height || right < 0 || r.left > view.width;
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
function onScroll(ev: Event): void {
|
|
@@ -126,7 +147,8 @@ function onPointerDown(ev: PointerEvent): void {
|
|
|
126
147
|
if (layer.owns(path)) return;
|
|
127
148
|
if (panelEl.value && path.includes(panelEl.value)) return;
|
|
128
149
|
if (anchorEl.value && path.includes(anchorEl.value)) return;
|
|
129
|
-
|
|
150
|
+
const own = anchorElement();
|
|
151
|
+
if (own && path.includes(own)) return;
|
|
130
152
|
setOpen(false);
|
|
131
153
|
}
|
|
132
154
|
|
|
@@ -137,7 +159,8 @@ function onKeydown(ev: KeyboardEvent): void {
|
|
|
137
159
|
ev.preventDefault();
|
|
138
160
|
ev.stopPropagation();
|
|
139
161
|
setOpen(false);
|
|
140
|
-
trigger()
|
|
162
|
+
const t = trigger();
|
|
163
|
+
if (t instanceof HTMLElement) t.focus();
|
|
141
164
|
}
|
|
142
165
|
|
|
143
166
|
function listen(on: boolean): void {
|
|
@@ -183,6 +206,7 @@ defineExpose({ place });
|
|
|
183
206
|
:role="role"
|
|
184
207
|
:aria-label="label"
|
|
185
208
|
tabindex="-1"
|
|
209
|
+
v-bind="$attrs"
|
|
186
210
|
:style="{
|
|
187
211
|
left: `${pos.x}px`,
|
|
188
212
|
top: `${pos.y}px`,
|
package/src/Textarea.vue
CHANGED
|
@@ -10,6 +10,10 @@ interface Props {
|
|
|
10
10
|
error?: boolean | undefined;
|
|
11
11
|
id?: string | undefined;
|
|
12
12
|
ariaLabel?: string | undefined;
|
|
13
|
+
/** Mono text: templates, tag lists. */
|
|
14
|
+
mono?: boolean | undefined;
|
|
15
|
+
/** "accent" marks a value changed from its default, "warn" one that needs a look. */
|
|
16
|
+
tone?: "accent" | "warn" | undefined;
|
|
13
17
|
}
|
|
14
18
|
const props = withDefaults(defineProps<Props>(), { rows: 3 });
|
|
15
19
|
|
|
@@ -44,6 +48,8 @@ watch(() => props.modelValue, () => {
|
|
|
44
48
|
:id="id"
|
|
45
49
|
ref="taRef"
|
|
46
50
|
class="wp-textarea"
|
|
51
|
+
:data-mono="mono || undefined"
|
|
52
|
+
:data-tone="tone"
|
|
47
53
|
:rows="rows"
|
|
48
54
|
:value="modelValue"
|
|
49
55
|
:placeholder="placeholder"
|
|
@@ -55,3 +61,5 @@ watch(() => props.modelValue, () => {
|
|
|
55
61
|
@focus="(e) => emit('focus', e)"
|
|
56
62
|
/>
|
|
57
63
|
</template>
|
|
64
|
+
|
|
65
|
+
<style scoped src="./input.css"></style>
|
package/src/floating.ts
CHANGED
|
@@ -17,6 +17,18 @@ export interface Rect {
|
|
|
17
17
|
height: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/** Something a panel can be placed against without being an element: the
|
|
21
|
+
* text caret, a selection, a point the pointer was at. */
|
|
22
|
+
export interface VirtualAnchor {
|
|
23
|
+
getBoundingClientRect(): Rect;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A virtual anchor for a fixed rectangle (a point is width and height 0). */
|
|
27
|
+
export function rectAnchor(rect: Rect): VirtualAnchor {
|
|
28
|
+
const r = { left: rect.left, top: rect.top, width: rect.width, height: rect.height };
|
|
29
|
+
return { getBoundingClientRect: () => r };
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
export interface Size {
|
|
21
33
|
width: number;
|
|
22
34
|
height: number;
|
package/src/input.css
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* Input and Textarea: the text field and the box around a prefixed one
|
|
2
|
+
* (`.wp-input-group`: an icon, a `$` prefix, a unit or the readonly lock).
|
|
3
|
+
* Scoped into Input.vue and Textarea.vue so a field looks right on the
|
|
4
|
+
* canvas, which never loads theme/tokens.css; the theme imports it too so
|
|
5
|
+
* a page can still write `.wp-input-group` around another field. Shared
|
|
6
|
+
* tokens only. The other field pieces draw field.css' `.wp-field-box`.
|
|
7
|
+
*
|
|
8
|
+
* `data-mono` sets the text in the mono face (variable names, ids, tags);
|
|
9
|
+
* `data-tone="accent"` marks a value changed from its default (accent border
|
|
10
|
+
* and text), `data-tone="warn"` one that needs a look (an empty binding). */
|
|
11
|
+
|
|
12
|
+
.wp-input,
|
|
13
|
+
.wp-textarea,
|
|
14
|
+
.wp-input-group { box-sizing: border-box; }
|
|
15
|
+
|
|
16
|
+
.wp-input,
|
|
17
|
+
.wp-textarea {
|
|
18
|
+
height: var(--wp-control-h-md);
|
|
19
|
+
width: 100%;
|
|
20
|
+
background: var(--wp-bg-2);
|
|
21
|
+
color: var(--wp-text);
|
|
22
|
+
border: 1px solid var(--wp-border);
|
|
23
|
+
border-radius: var(--wp-radius-sm);
|
|
24
|
+
padding: 0 10px;
|
|
25
|
+
font-size: 12.5px;
|
|
26
|
+
outline: none;
|
|
27
|
+
transition: border-color var(--wp-motion-quick), background var(--wp-motion-quick), box-shadow var(--wp-motion-quick);
|
|
28
|
+
}
|
|
29
|
+
.wp-textarea {
|
|
30
|
+
height: auto;
|
|
31
|
+
padding: 8px 10px;
|
|
32
|
+
resize: vertical;
|
|
33
|
+
min-height: var(--wp-control-h-md);
|
|
34
|
+
line-height: 1.5;
|
|
35
|
+
}
|
|
36
|
+
.wp-input:focus-visible,
|
|
37
|
+
.wp-textarea:focus-visible {
|
|
38
|
+
border-color: var(--wp-accent-500);
|
|
39
|
+
box-shadow: var(--wp-focus-ring);
|
|
40
|
+
background: var(--wp-bg-1);
|
|
41
|
+
}
|
|
42
|
+
/* Retain :focus fallback for browsers/contexts that don't support :focus-visible */
|
|
43
|
+
.wp-input:focus,
|
|
44
|
+
.wp-textarea:focus {
|
|
45
|
+
border-color: var(--wp-accent-500);
|
|
46
|
+
box-shadow: var(--wp-focus-ring);
|
|
47
|
+
background: var(--wp-bg-1);
|
|
48
|
+
}
|
|
49
|
+
/* Error state on standalone controls (when not wrapped in .wp-field) */
|
|
50
|
+
.wp-input[aria-invalid="true"],
|
|
51
|
+
.wp-textarea[aria-invalid="true"] {
|
|
52
|
+
border-color: var(--wp-error-border);
|
|
53
|
+
}
|
|
54
|
+
.wp-input::placeholder, .wp-textarea::placeholder { color: var(--wp-text-dim); }
|
|
55
|
+
.wp-input--sm { height: var(--wp-control-h-sm); font-size: 12px; padding: 0 8px; }
|
|
56
|
+
.wp-input--lg { height: var(--wp-control-h-lg); font-size: 13.5px; padding: 0 12px; }
|
|
57
|
+
/* Readonly: shown, not editable. Dimmer fill, no focus ring (Input adds a lock). */
|
|
58
|
+
.wp-input[readonly],
|
|
59
|
+
.wp-textarea[readonly] {
|
|
60
|
+
background: var(--wp-bg-1);
|
|
61
|
+
color: var(--wp-text-muted);
|
|
62
|
+
border-style: dashed;
|
|
63
|
+
cursor: default;
|
|
64
|
+
}
|
|
65
|
+
.wp-input[readonly]:focus,
|
|
66
|
+
.wp-input[readonly]:focus-visible,
|
|
67
|
+
.wp-textarea[readonly]:focus,
|
|
68
|
+
.wp-textarea[readonly]:focus-visible { box-shadow: none; border-color: var(--wp-border-strong); background: var(--wp-bg-1); }
|
|
69
|
+
.wp-input:disabled,
|
|
70
|
+
.wp-textarea:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
71
|
+
|
|
72
|
+
.wp-input-group {
|
|
73
|
+
display: inline-flex; align-items: stretch; width: 100%;
|
|
74
|
+
border: 1px solid var(--wp-border); border-radius: var(--wp-radius-sm);
|
|
75
|
+
background: var(--wp-bg-2);
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
height: var(--wp-control-h-md);
|
|
78
|
+
min-height: var(--wp-control-h-md);
|
|
79
|
+
flex-shrink: 0;
|
|
80
|
+
transition: border-color var(--wp-motion-quick), box-shadow var(--wp-motion-quick);
|
|
81
|
+
}
|
|
82
|
+
.wp-input-group:focus-within {
|
|
83
|
+
border-color: var(--wp-accent-500);
|
|
84
|
+
box-shadow: var(--wp-focus-ring);
|
|
85
|
+
}
|
|
86
|
+
/* Inline icon and prefix/suffix text: no box, no divider, sitting in the
|
|
87
|
+
* field's own padding so "$" reads as part of "$hair". */
|
|
88
|
+
.wp-input-group__icon,
|
|
89
|
+
.wp-input-group__affix,
|
|
90
|
+
.wp-input-group__lock {
|
|
91
|
+
display: inline-flex; align-items: center; flex-shrink: 0;
|
|
92
|
+
color: var(--wp-text-dim);
|
|
93
|
+
user-select: none;
|
|
94
|
+
}
|
|
95
|
+
.wp-input-group__icon { padding-left: 10px; }
|
|
96
|
+
.wp-input-group__icon .pi { font-size: 14px; line-height: 1; }
|
|
97
|
+
.wp-input-group__affix { font-family: var(--wp-font-mono); font-size: 12.5px; }
|
|
98
|
+
.wp-input-group__affix--prefix { padding-left: 10px; }
|
|
99
|
+
.wp-input-group__affix--suffix { padding-right: 10px; }
|
|
100
|
+
.wp-input-group__lock { padding-right: 10px; }
|
|
101
|
+
.wp-input-group__icon + .wp-input,
|
|
102
|
+
.wp-input-group__icon + .wp-input-group__affix--prefix { padding-left: 7px; }
|
|
103
|
+
.wp-input-group__affix--prefix + .wp-input { padding-left: 2px; }
|
|
104
|
+
.wp-input-group .wp-input:has(+ .wp-input-group__affix--suffix),
|
|
105
|
+
.wp-input-group .wp-input:has(+ .wp-input-group__lock) { padding-right: 4px; }
|
|
106
|
+
.wp-input-group--sm { height: var(--wp-control-h-sm); min-height: var(--wp-control-h-sm); }
|
|
107
|
+
.wp-input-group--lg { height: var(--wp-control-h-lg); min-height: var(--wp-control-h-lg); }
|
|
108
|
+
.wp-input-group--readonly { background: var(--wp-bg-1); border-style: dashed; }
|
|
109
|
+
.wp-input-group--readonly:focus-within { box-shadow: none; border-color: var(--wp-border-strong); }
|
|
110
|
+
.wp-input-group--readonly .wp-input[readonly] { border: none; }
|
|
111
|
+
.wp-input-group--disabled { opacity: 0.45; cursor: not-allowed; }
|
|
112
|
+
.wp-input-group--disabled .wp-input:disabled { opacity: 1; }
|
|
113
|
+
.wp-input-group[aria-invalid="true"] { border-color: var(--wp-error-border); }
|
|
114
|
+
.wp-input-group .wp-input {
|
|
115
|
+
border: none !important;
|
|
116
|
+
background: transparent !important;
|
|
117
|
+
border-radius: 0;
|
|
118
|
+
height: 100%;
|
|
119
|
+
flex: 1;
|
|
120
|
+
}
|
|
121
|
+
.wp-input-group .wp-input:focus { box-shadow: none; }
|
|
122
|
+
|
|
123
|
+
/* Mono text and tones (see the top of this file). On a prefixed field the
|
|
124
|
+
* group draws the border, so the tone sits on the group. */
|
|
125
|
+
.wp-input[data-mono],
|
|
126
|
+
.wp-textarea[data-mono] { font-family: var(--wp-font-mono); font-size: 12px; }
|
|
127
|
+
.wp-input--sm[data-mono] { font-size: 11px; }
|
|
128
|
+
.wp-input[data-tone="accent"],
|
|
129
|
+
.wp-textarea[data-tone="accent"],
|
|
130
|
+
.wp-input-group[data-tone="accent"] { border-color: var(--wp-accent-500); }
|
|
131
|
+
.wp-input[data-tone="accent"],
|
|
132
|
+
.wp-textarea[data-tone="accent"],
|
|
133
|
+
.wp-input-group[data-tone="accent"] .wp-input,
|
|
134
|
+
.wp-input-group[data-tone="accent"] .wp-input-group__affix { color: var(--wp-accent-text); }
|
|
135
|
+
.wp-input[data-tone="warn"],
|
|
136
|
+
.wp-textarea[data-tone="warn"],
|
|
137
|
+
.wp-input-group[data-tone="warn"] { border-color: var(--wp-warn); }
|
|
138
|
+
.wp-input-group[data-tone="warn"] .wp-input-group__affix { color: var(--wp-warn); }
|
package/src/theme/tokens.css
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
@import "../input.css";
|
|
1
2
|
/* =========================================================
|
|
2
3
|
Wildcard Pipeline — Design Tokens & Base
|
|
3
4
|
PrimeVue 4 Aura-inspired, dark, brand-customized.
|
|
@@ -704,10 +705,11 @@ input, textarea, select { font: inherit; }
|
|
|
704
705
|
* canvas Button (component standard, rule 8). main.ts loads it right after
|
|
705
706
|
* this file. */
|
|
706
707
|
|
|
707
|
-
/* ---------- Inputs ----------
|
|
708
|
-
.wp-input
|
|
709
|
-
.
|
|
710
|
-
.
|
|
708
|
+
/* ---------- Inputs ----------
|
|
709
|
+
* Input and Textarea (and the .wp-input-group around a prefixed field) are
|
|
710
|
+
* drawn by ../input.css, imported at the top of this file and scoped into
|
|
711
|
+
* Input.vue / Textarea.vue so they look the same on the canvas. */
|
|
712
|
+
.wp-select {
|
|
711
713
|
height: var(--wp-input-h);
|
|
712
714
|
width: 100%;
|
|
713
715
|
background: var(--wp-bg-2);
|
|
@@ -719,51 +721,14 @@ input, textarea, select { font: inherit; }
|
|
|
719
721
|
outline: none;
|
|
720
722
|
transition: border-color .12s, background .12s, box-shadow .12s;
|
|
721
723
|
}
|
|
722
|
-
.wp-textarea {
|
|
723
|
-
height: auto;
|
|
724
|
-
padding: 8px 10px;
|
|
725
|
-
resize: vertical;
|
|
726
|
-
min-height: var(--wp-input-h);
|
|
727
|
-
line-height: 1.5;
|
|
728
|
-
}
|
|
729
|
-
.wp-input:focus-visible,
|
|
730
724
|
.wp-select:focus-visible,
|
|
731
|
-
.wp-
|
|
725
|
+
.wp-select:focus {
|
|
732
726
|
border-color: var(--wp-accent-500);
|
|
733
727
|
box-shadow: var(--wp-focus-ring);
|
|
734
728
|
background: var(--wp-bg-1);
|
|
735
729
|
}
|
|
736
|
-
|
|
737
|
-
.wp-input:
|
|
738
|
-
.wp-select:focus,
|
|
739
|
-
.wp-textarea:focus {
|
|
740
|
-
border-color: var(--wp-accent-500);
|
|
741
|
-
box-shadow: var(--wp-focus-ring);
|
|
742
|
-
background: var(--wp-bg-1);
|
|
743
|
-
}
|
|
744
|
-
/* Error state on standalone controls (when not wrapped in .wp-field) */
|
|
745
|
-
.wp-input[aria-invalid="true"],
|
|
746
|
-
.wp-select[aria-invalid="true"],
|
|
747
|
-
.wp-textarea[aria-invalid="true"] {
|
|
748
|
-
border-color: var(--wp-error-border);
|
|
749
|
-
}
|
|
750
|
-
.wp-input::placeholder, .wp-textarea::placeholder { color: var(--wp-text-dim); }
|
|
751
|
-
.wp-input--sm, .wp-select--sm { height: var(--wp-input-h-sm); font-size: 12px; padding: 0 8px; }
|
|
752
|
-
.wp-input--lg { height: var(--wp-control-h-lg); font-size: 13.5px; padding: 0 12px; }
|
|
753
|
-
/* Readonly: shown, not editable. Dimmer fill, no focus ring (Input adds a lock). */
|
|
754
|
-
.wp-input[readonly],
|
|
755
|
-
.wp-textarea[readonly] {
|
|
756
|
-
background: var(--wp-bg-1);
|
|
757
|
-
color: var(--wp-text-muted);
|
|
758
|
-
border-style: dashed;
|
|
759
|
-
cursor: default;
|
|
760
|
-
}
|
|
761
|
-
.wp-input[readonly]:focus,
|
|
762
|
-
.wp-input[readonly]:focus-visible,
|
|
763
|
-
.wp-textarea[readonly]:focus,
|
|
764
|
-
.wp-textarea[readonly]:focus-visible { box-shadow: none; border-color: var(--wp-border-strong); background: var(--wp-bg-1); }
|
|
765
|
-
.wp-input:disabled,
|
|
766
|
-
.wp-textarea:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
730
|
+
.wp-select[aria-invalid="true"] { border-color: var(--wp-error-border); }
|
|
731
|
+
.wp-select--sm { height: var(--wp-input-h-sm); font-size: 12px; padding: 0 8px; }
|
|
767
732
|
/* Custom select trigger button — flex layout so label + spacer + chevron align. */
|
|
768
733
|
.wp-select {
|
|
769
734
|
display: flex;
|
|
@@ -793,56 +758,7 @@ input, textarea, select { font: inherit; }
|
|
|
793
758
|
border-color: var(--wp-error-border);
|
|
794
759
|
}
|
|
795
760
|
|
|
796
|
-
|
|
797
|
-
display: inline-flex; align-items: stretch; width: 100%;
|
|
798
|
-
border: 1px solid var(--wp-border); border-radius: var(--wp-radius-sm);
|
|
799
|
-
background: var(--wp-bg-2);
|
|
800
|
-
overflow: hidden;
|
|
801
|
-
height: var(--wp-input-h);
|
|
802
|
-
min-height: var(--wp-input-h);
|
|
803
|
-
flex-shrink: 0;
|
|
804
|
-
transition: border-color .12s, box-shadow .12s;
|
|
805
|
-
}
|
|
806
|
-
.wp-input-group:focus-within {
|
|
807
|
-
border-color: var(--wp-accent-500);
|
|
808
|
-
box-shadow: var(--wp-focus-ring);
|
|
809
|
-
}
|
|
810
|
-
/* Inline icon and prefix/suffix text: no box, no divider, sitting in the
|
|
811
|
-
* field's own padding so "$" reads as part of "$hair". */
|
|
812
|
-
.wp-input-group__icon,
|
|
813
|
-
.wp-input-group__affix,
|
|
814
|
-
.wp-input-group__lock {
|
|
815
|
-
display: inline-flex; align-items: center; flex-shrink: 0;
|
|
816
|
-
color: var(--wp-text-dim);
|
|
817
|
-
user-select: none;
|
|
818
|
-
}
|
|
819
|
-
.wp-input-group__icon { padding-left: 10px; }
|
|
820
|
-
.wp-input-group__icon .pi { font-size: 14px; line-height: 1; }
|
|
821
|
-
.wp-input-group__affix { font-family: var(--wp-font-mono); font-size: 12.5px; }
|
|
822
|
-
.wp-input-group__affix--prefix { padding-left: 10px; }
|
|
823
|
-
.wp-input-group__affix--suffix { padding-right: 10px; }
|
|
824
|
-
.wp-input-group__lock { padding-right: 10px; }
|
|
825
|
-
.wp-input-group__icon + .wp-input,
|
|
826
|
-
.wp-input-group__icon + .wp-input-group__affix--prefix { padding-left: 7px; }
|
|
827
|
-
.wp-input-group__affix--prefix + .wp-input { padding-left: 2px; }
|
|
828
|
-
.wp-input-group .wp-input:has(+ .wp-input-group__affix--suffix),
|
|
829
|
-
.wp-input-group .wp-input:has(+ .wp-input-group__lock) { padding-right: 4px; }
|
|
830
|
-
.wp-input-group--sm { height: var(--wp-control-h-sm); min-height: var(--wp-control-h-sm); }
|
|
831
|
-
.wp-input-group--lg { height: var(--wp-control-h-lg); min-height: var(--wp-control-h-lg); }
|
|
832
|
-
.wp-input-group--readonly { background: var(--wp-bg-1); border-style: dashed; }
|
|
833
|
-
.wp-input-group--readonly:focus-within { box-shadow: none; border-color: var(--wp-border-strong); }
|
|
834
|
-
.wp-input-group--readonly .wp-input[readonly] { border: none; }
|
|
835
|
-
.wp-input-group--disabled { opacity: 0.45; cursor: not-allowed; }
|
|
836
|
-
.wp-input-group--disabled .wp-input:disabled { opacity: 1; }
|
|
837
|
-
.wp-input-group[aria-invalid="true"] { border-color: var(--wp-error-border); }
|
|
838
|
-
.wp-input-group .wp-input {
|
|
839
|
-
border: none !important;
|
|
840
|
-
background: transparent !important;
|
|
841
|
-
border-radius: 0;
|
|
842
|
-
height: 100%;
|
|
843
|
-
flex: 1;
|
|
844
|
-
}
|
|
845
|
-
.wp-input-group .wp-input:focus { box-shadow: none; }
|
|
761
|
+
|
|
846
762
|
|
|
847
763
|
/* ---------- Chips / tags ---------- */
|
|
848
764
|
.wp-chip {
|
package/src/tokens.ts
CHANGED
|
@@ -58,8 +58,8 @@ export const LOCAL_TOKENS = ["--wp-hsv-hue", "--wp-lm-o", "--wp-select-line-h",
|
|
|
58
58
|
export const PAGE_PIECES = [
|
|
59
59
|
"axis-color.ts", "kind-colour.ts", "matrix-axis.ts", "BundleMeta.vue", "ConstraintMatrixPreview.vue", "PostCard.vue", "TokenizedValue.vue",
|
|
60
60
|
"Card.vue", "ChoiceCards.vue",
|
|
61
|
-
"DataTable.vue", "Drawer.vue", "EmptyState.vue", "Field.vue", "Icon.vue",
|
|
62
|
-
"ListFilter.vue", "RelativeDate.vue", "Select.vue", "
|
|
61
|
+
"DataTable.vue", "Drawer.vue", "EmptyState.vue", "Field.vue", "Icon.vue",
|
|
62
|
+
"ListFilter.vue", "RelativeDate.vue", "Select.vue", "ToastHost.vue",
|
|
63
63
|
// The payload previews and post loading shapes (community post pages).
|
|
64
64
|
"PayloadPreview.vue", "WildcardPreview.vue", "FixedValuesPreview.vue", "CombinePreview.vue", "DerivationPreview.vue",
|
|
65
65
|
"DerivationCondition.vue", "ConstraintPreview.vue", "BundlePreview.vue", "PreviewGroup.vue", "preview.css", "RichText.vue",
|