@dumiflex/wp-kit 0.5.0 → 0.6.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 +32 -0
- package/README.md +5 -0
- package/package.json +4 -3
- package/src/Avatar.vue +1 -1
- package/src/Banner.vue +4 -4
- package/src/Button.vue +73 -44
- package/src/Checkbox.vue +201 -63
- package/src/Chip.vue +5 -2
- package/src/CodeBlock.vue +1 -1
- package/src/ColorField.vue +60 -6
- package/src/Combobox.vue +10 -14
- package/src/ConfirmModal.vue +12 -22
- package/src/ContextMenu.vue +11 -30
- package/src/CopyButton.vue +1 -1
- package/src/DataTable.vue +0 -2
- package/src/DatePicker.vue +1 -1
- package/src/Disclosure.vue +2 -1
- package/src/Drawer.vue +2 -2
- package/src/DropZone.vue +2 -2
- package/src/HsvPicker.vue +2 -2
- package/src/IconPicker.vue +1 -1
- package/src/ImageGrid.vue +2 -2
- package/src/Lightbox.vue +2 -2
- package/src/Modal.vue +109 -44
- package/src/ModalShell.vue +26 -5
- package/src/Pagination.vue +4 -4
- package/src/Popover.vue +68 -12
- package/src/RadioGroup.vue +5 -4
- package/src/SearchInput.vue +1 -1
- package/src/Select.vue +53 -158
- package/src/Skeleton.vue +1 -1
- package/src/Slider.vue +166 -55
- package/src/SortableList.vue +1 -1
- package/src/TagInput.vue +1 -1
- package/src/Timeline.vue +2 -2
- package/src/WorkingLine.vue +320 -0
- package/src/WpIconButton.vue +3 -2
- package/src/WpImageZoom.vue +7 -7
- package/src/WpSegmented.vue +3 -1
- package/src/WpSwitch.vue +1 -1
- package/src/WpTabs.vue +1 -1
- package/src/_modal-motion.css +1 -1
- package/src/button.css +8 -6
- package/src/floating.ts +5 -0
- package/src/hsv.ts +1 -1
- package/src/menu.css +51 -0
- package/src/motion.css +73 -0
- package/src/slider-math.ts +45 -1
- package/src/theme/looks.css +6 -19
- package/src/theme/tokens.css +12 -1108
- package/src/tokens.ts +8 -3
- package/src/tooltip.css +8 -5
- package/src/useDialogFocus.ts +11 -2
- package/src/CloseButton.vue +0 -69
- package/src/ColorPicker.vue +0 -201
- package/src/ConfirmDialog.vue +0 -165
- package/src/RangeSlider.vue +0 -183
- package/src/Toggle.vue +0 -36
- package/src/WpButton.vue +0 -75
- package/src/WpCheck.vue +0 -104
- package/src/_skeleton.css +0 -50
package/src/ColorField.vue
CHANGED
|
@@ -8,14 +8,20 @@
|
|
|
8
8
|
* so it follows the swatch on scroll and closes on Escape or a click
|
|
9
9
|
* outside. Works on the canvas (`size="sm"`) and in the manager.
|
|
10
10
|
*
|
|
11
|
+
* `presets` adds a row of preset swatches under the square (`true` for the
|
|
12
|
+
* kit's COLOUR_PRESETS, or a list of hex colours); a click takes one and
|
|
13
|
+
* closes. `compact` shows the swatch alone, for a tight spot (a name row).
|
|
14
|
+
*
|
|
11
15
|
* <ColorField v-model="labelColor" alpha allow-none size="sm" aria-label="Label band" />
|
|
16
|
+
* <ColorField v-model="color" presets size="md" aria-label="Category colour" />
|
|
12
17
|
*/
|
|
13
18
|
import { computed, ref, watch } from "vue";
|
|
14
19
|
import HsvPicker from "./HsvPicker.vue";
|
|
15
20
|
import Popover from "./Popover.vue";
|
|
16
21
|
import WpNumber from "./WpNumber.vue";
|
|
17
|
-
import
|
|
22
|
+
import Button from "./Button.vue";
|
|
18
23
|
import { rgbToHex } from "./hsv";
|
|
24
|
+
import { COLOUR_PRESETS } from "./colour-presets";
|
|
19
25
|
|
|
20
26
|
const props = withDefaults(defineProps<{
|
|
21
27
|
modelValue: string;
|
|
@@ -23,11 +29,15 @@ const props = withDefaults(defineProps<{
|
|
|
23
29
|
alpha?: boolean | undefined;
|
|
24
30
|
/** Offer "None" (writes `none`). */
|
|
25
31
|
allowNone?: boolean | undefined;
|
|
32
|
+
/** Preset swatches: `true` for COLOUR_PRESETS, or your own hex list. */
|
|
33
|
+
presets?: readonly string[] | boolean | undefined;
|
|
34
|
+
/** The swatch alone, without the value beside it. */
|
|
35
|
+
compact?: boolean | undefined;
|
|
26
36
|
size?: "sm" | "md" | undefined;
|
|
27
37
|
ariaLabel?: string | undefined;
|
|
28
38
|
disabled?: boolean | undefined;
|
|
29
39
|
dataTest?: string | undefined;
|
|
30
|
-
}>(), { alpha: false, allowNone: false, size: "sm", ariaLabel: "Colour", disabled: false, dataTest: undefined });
|
|
40
|
+
}>(), { alpha: false, allowNone: false, presets: false, compact: false, size: "sm", ariaLabel: "Colour", disabled: false, dataTest: undefined });
|
|
31
41
|
|
|
32
42
|
const emit = defineEmits<{ "update:modelValue": [value: string] }>();
|
|
33
43
|
|
|
@@ -79,6 +89,17 @@ function onDraft(ev: Event): void {
|
|
|
79
89
|
const p = parse(v);
|
|
80
90
|
if (p) emit("update:modelValue", compose(p.rgb, props.alpha ? p.alpha : 255));
|
|
81
91
|
}
|
|
92
|
+
const presetList = computed<readonly string[]>(() => {
|
|
93
|
+
if (props.presets === true) return COLOUR_PRESETS;
|
|
94
|
+
return Array.isArray(props.presets) ? props.presets : [];
|
|
95
|
+
});
|
|
96
|
+
function isActivePreset(preset: string): boolean {
|
|
97
|
+
return !isNone.value && preset.trim().toLowerCase() === rgb.value && alphaValue.value === 255;
|
|
98
|
+
}
|
|
99
|
+
function pickPreset(preset: string): void {
|
|
100
|
+
emit("update:modelValue", preset);
|
|
101
|
+
open.value = false;
|
|
102
|
+
}
|
|
82
103
|
function pickNone(): void {
|
|
83
104
|
emit("update:modelValue", "none");
|
|
84
105
|
open.value = false;
|
|
@@ -92,7 +113,7 @@ function pickNone(): void {
|
|
|
92
113
|
v-bind="triggerProps"
|
|
93
114
|
type="button"
|
|
94
115
|
class="wp-color-field wp-field-box"
|
|
95
|
-
:class="[`wp-color-field--${size}`, `wp-field-box--${size}`, { 'is-none': isNone }]"
|
|
116
|
+
:class="[`wp-color-field--${size}`, `wp-field-box--${size}`, { 'is-none': isNone, 'wp-color-field--compact': compact }]"
|
|
96
117
|
:disabled="disabled"
|
|
97
118
|
:aria-label="`${ariaLabel}: ${shown}`"
|
|
98
119
|
:data-test="dataTest"
|
|
@@ -100,7 +121,7 @@ function pickNone(): void {
|
|
|
100
121
|
<span class="wp-color-field__chip" aria-hidden="true">
|
|
101
122
|
<span class="wp-color-field__fill" :style="{ background: swatch }" />
|
|
102
123
|
</span>
|
|
103
|
-
<span class="wp-color-field__value" aria-hidden="true">{{ shown }}</span>
|
|
124
|
+
<span v-if="!compact" class="wp-color-field__value" aria-hidden="true">{{ shown }}</span>
|
|
104
125
|
</button>
|
|
105
126
|
</template>
|
|
106
127
|
<div class="wp-color-field__panel" @keydown.stop>
|
|
@@ -128,7 +149,21 @@ function pickNone(): void {
|
|
|
128
149
|
@update:model-value="onOpacity"
|
|
129
150
|
/>
|
|
130
151
|
</div>
|
|
131
|
-
<
|
|
152
|
+
<div v-if="presetList.length" class="wp-color-field__presets" role="group" :aria-label="`${ariaLabel} presets`">
|
|
153
|
+
<button
|
|
154
|
+
v-for="preset in presetList"
|
|
155
|
+
:key="preset"
|
|
156
|
+
type="button"
|
|
157
|
+
class="wp-color-field__preset"
|
|
158
|
+
:style="{ background: preset }"
|
|
159
|
+
:title="preset"
|
|
160
|
+
:aria-pressed="isActivePreset(preset)"
|
|
161
|
+
:aria-label="`Use ${preset}`"
|
|
162
|
+
:data-test="dataTest ? `${dataTest}-preset` : undefined"
|
|
163
|
+
@click="pickPreset(preset)"
|
|
164
|
+
/>
|
|
165
|
+
</div>
|
|
166
|
+
<Button
|
|
132
167
|
v-if="allowNone"
|
|
133
168
|
size="sm"
|
|
134
169
|
variant="ghost"
|
|
@@ -136,7 +171,7 @@ function pickNone(): void {
|
|
|
136
171
|
:pressed="isNone"
|
|
137
172
|
:data-test="dataTest ? `${dataTest}-none` : undefined"
|
|
138
173
|
@click="pickNone"
|
|
139
|
-
>None</
|
|
174
|
+
>None</Button>
|
|
140
175
|
</div>
|
|
141
176
|
</Popover>
|
|
142
177
|
</template>
|
|
@@ -165,6 +200,8 @@ function pickNone(): void {
|
|
|
165
200
|
/* Checks show through a see-through colour. */
|
|
166
201
|
background: repeating-conic-gradient(var(--wp-bg-4) 0 25%, var(--wp-bg-1) 0 50%) 0 0 / 8px 8px;
|
|
167
202
|
}
|
|
203
|
+
.wp-color-field--md .wp-color-field__chip { width: 20px; height: 20px; border-radius: min(4px, var(--wp-radius-cap, 999px)); }
|
|
204
|
+
.wp-color-field--compact { padding: 0 3px; }
|
|
168
205
|
.wp-color-field__fill { position: absolute; inset: 0; }
|
|
169
206
|
.wp-color-field.is-none .wp-color-field__chip::after {
|
|
170
207
|
content: "";
|
|
@@ -185,4 +222,21 @@ function pickNone(): void {
|
|
|
185
222
|
.wp-color-field__row { display: flex; gap: 6px; align-items: center; }
|
|
186
223
|
.wp-color-field__hex { flex: 1; min-width: 0; height: var(--wp-field-h); padding: 0 6px; font-family: var(--wp-font-mono, monospace); }
|
|
187
224
|
.wp-color-field__alpha { width: 72px; flex: none; }
|
|
225
|
+
.wp-color-field__presets { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
|
|
226
|
+
.wp-color-field__preset {
|
|
227
|
+
width: 100%;
|
|
228
|
+
aspect-ratio: 1 / 1;
|
|
229
|
+
min-width: 0;
|
|
230
|
+
padding: 0;
|
|
231
|
+
border: 1px solid var(--wp-border);
|
|
232
|
+
border-radius: min(4px, var(--wp-radius-cap, 999px));
|
|
233
|
+
cursor: pointer;
|
|
234
|
+
transition: transform var(--wp-motion-quick), box-shadow var(--wp-motion-quick);
|
|
235
|
+
}
|
|
236
|
+
.wp-color-field__preset:hover { transform: scale(1.08); }
|
|
237
|
+
.wp-color-field__preset:focus-visible { outline: none; box-shadow: var(--wp-focus-ring); }
|
|
238
|
+
.wp-color-field__preset[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--wp-bg-1), 0 0 0 4px var(--wp-accent-500); }
|
|
239
|
+
@media (prefers-reduced-motion: reduce) {
|
|
240
|
+
.wp-color-field__preset { transition: none; }
|
|
241
|
+
}
|
|
188
242
|
</style>
|
package/src/Combobox.vue
CHANGED
|
@@ -175,7 +175,7 @@ function setOpen(v: boolean): void {
|
|
|
175
175
|
<!-- Popover's autofocus prop is off here: focus stays in the box you type in. -->
|
|
176
176
|
<!-- eslint-disable vuejs-accessibility/no-autofocus -->
|
|
177
177
|
<template>
|
|
178
|
-
<Popover :open="open" role="group" match-width :autofocus="false" :label="`${ariaLabel} choices`" @update:open="setOpen">
|
|
178
|
+
<Popover :open="open" variant="menu" role="group" match-width :max-height="280" :autofocus="false" :label="`${ariaLabel} choices`" @update:open="setOpen">
|
|
179
179
|
<template #trigger>
|
|
180
180
|
<!-- A click on the box's padding focuses the text field, which has the keys. -->
|
|
181
181
|
<!-- eslint-disable-next-line vuejs-accessibility/click-events-have-key-events, vuejs-accessibility/no-static-element-interactions -->
|
|
@@ -245,7 +245,7 @@ function setOpen(v: boolean): void {
|
|
|
245
245
|
v-for="(o, i) in matches"
|
|
246
246
|
:id="`${listId}-${i}`"
|
|
247
247
|
:key="String(o.value)"
|
|
248
|
-
class="wp-combo__opt"
|
|
248
|
+
class="wp-combo__opt wp-menu__item"
|
|
249
249
|
role="option"
|
|
250
250
|
:aria-selected="isPicked(o.value)"
|
|
251
251
|
:aria-disabled="o.disabled || undefined"
|
|
@@ -265,6 +265,7 @@ function setOpen(v: boolean): void {
|
|
|
265
265
|
</template>
|
|
266
266
|
|
|
267
267
|
<style scoped src="./field.css"></style>
|
|
268
|
+
<style scoped src="./menu.css"></style>
|
|
268
269
|
|
|
269
270
|
<style scoped>
|
|
270
271
|
.wp-combo {
|
|
@@ -284,7 +285,7 @@ function setOpen(v: boolean): void {
|
|
|
284
285
|
color: var(--wp-text); font-size: var(--wp-text-xs); line-height: 1;
|
|
285
286
|
}
|
|
286
287
|
.wp-combo[data-size="sm"] .wp-combo__chip { height: 16px; padding-left: 5px; font-size: 10.5px; }
|
|
287
|
-
.wp-combo__chip-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
288
|
+
.wp-combo__chip-text { min-width: 0; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
288
289
|
.wp-combo__remove, .wp-combo__clear {
|
|
289
290
|
display: inline-grid; place-items: center; width: 16px; height: 16px; padding: 0;
|
|
290
291
|
border: 0; border-radius: min(50%, var(--wp-radius-cap, 999px)); background: transparent; color: var(--wp-text-muted); cursor: pointer;
|
|
@@ -299,7 +300,7 @@ function setOpen(v: boolean): void {
|
|
|
299
300
|
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
|
|
300
301
|
color: var(--wp-text-muted); font-size: 10px; pointer-events: none;
|
|
301
302
|
}
|
|
302
|
-
.wp-combo[data-size="sm"] .wp-combo__caret { right: 8px; font-size:
|
|
303
|
+
.wp-combo[data-size="sm"] .wp-combo__caret { right: 8px; font-size: 10px; }
|
|
303
304
|
.wp-combo__field {
|
|
304
305
|
flex: 1 0 6ch; min-width: 6ch; height: calc(var(--_h) - 8px); margin: 0; padding: 0 2px;
|
|
305
306
|
border: 0; outline: none; background: transparent; color: inherit; font: inherit;
|
|
@@ -311,20 +312,15 @@ function setOpen(v: boolean): void {
|
|
|
311
312
|
position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
|
|
312
313
|
clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
313
314
|
}
|
|
314
|
-
|
|
315
|
-
.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
color: var(--wp-text); font-size: var(--wp-text-sm); cursor: pointer;
|
|
319
|
-
}
|
|
320
|
-
.wp-combo__opt:hover, .wp-combo__opt[data-active] { background: var(--wp-row-hover); }
|
|
321
|
-
.wp-combo__opt[data-active] { box-shadow: inset 2px 0 0 var(--wp-accent); }
|
|
322
|
-
.wp-combo__opt[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; }
|
|
315
|
+
/* The panel and the rows' padding, corners, text and states are the shared
|
|
316
|
+
* menu look (Popover variant="menu" + menu.css); the list scrolls in the panel. */
|
|
317
|
+
.wp-combo__list { margin: 0; padding: 0; list-style: none; }
|
|
318
|
+
.wp-combo__opt { display: flex; align-items: flex-start; gap: var(--wp-space-2); }
|
|
323
319
|
.wp-combo__tick { width: 14px; margin-top: 2px; font-size: 10px; color: var(--wp-accent); visibility: hidden; }
|
|
324
320
|
.wp-combo__tick[data-on] { visibility: visible; }
|
|
325
321
|
.wp-combo__opt-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
326
322
|
.wp-combo__hint { color: var(--wp-text-muted); font-size: var(--wp-text-xs); }
|
|
327
|
-
.wp-combo__empty { padding: 6px
|
|
323
|
+
.wp-combo__empty { padding: 6px 8px; color: var(--wp-text-muted); font-size: 12px; }
|
|
328
324
|
@media (prefers-reduced-motion: reduce) {
|
|
329
325
|
.wp-combo { transition: none; }
|
|
330
326
|
}
|
package/src/ConfirmModal.vue
CHANGED
|
@@ -2,22 +2,23 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Ask before an action, in the manager's own dialog.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Built on Modal: the same backdrop, focus trap, Escape and focus return as
|
|
6
|
+
* every other dialog, with a Cancel button and a confirm button that names
|
|
7
|
+
* the action. One confirm for the manager, the canvas and the community site
|
|
8
|
+
* (ConfirmDialog was folded in: `visible` → `v-model:open`/`open`,
|
|
9
|
+
* `variant="danger"` → `tone="danger"`); it wears the colours of its side.
|
|
9
10
|
*
|
|
10
11
|
* Escape, the backdrop, the ✕ and Cancel all emit `cancel`. Enter confirms
|
|
11
12
|
* unless focus is on a button or a text box (which then gets the key itself).
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* Data-test names: `confirm-title`, `confirm-body`, `confirm-cancel`,
|
|
14
|
+
* `confirm-confirm`.
|
|
14
15
|
*
|
|
15
16
|
* `requireType` makes the user type that exact text before Confirm works
|
|
16
17
|
* (deleting an account); `confirmDisabled` holds Confirm off for a gate the
|
|
17
18
|
* page owns (a required reason in the slot). `warn` asks about something
|
|
18
19
|
* that can be undone but matters: an outlined button and an info mark.
|
|
19
20
|
*/
|
|
20
|
-
import { computed,
|
|
21
|
+
import { computed, ref, watch } from "vue";
|
|
21
22
|
import Modal from "./Modal.vue";
|
|
22
23
|
import Button from "./Button.vue";
|
|
23
24
|
import Icon from "./Icon.vue";
|
|
@@ -70,26 +71,15 @@ function onKeydown(ev: KeyboardEvent): void {
|
|
|
70
71
|
emit("confirm");
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
watch(
|
|
74
|
-
() => props.open,
|
|
75
|
-
(open) => {
|
|
76
|
-
if (open) {
|
|
77
|
-
typed.value = "";
|
|
78
|
-
window.addEventListener("keydown", onKeydown);
|
|
79
|
-
}
|
|
80
|
-
else window.removeEventListener("keydown", onKeydown);
|
|
81
|
-
},
|
|
82
|
-
{ immediate: true },
|
|
83
|
-
);
|
|
84
|
-
onBeforeUnmount(() => window.removeEventListener("keydown", onKeydown));
|
|
74
|
+
watch(() => props.open, (open) => { if (open) typed.value = ""; }, { immediate: true });
|
|
85
75
|
</script>
|
|
86
76
|
|
|
87
77
|
<template>
|
|
88
|
-
<Modal :open="open" size="sm" @update:open="onOpen">
|
|
78
|
+
<Modal :open="open" size="sm" @update:open="onOpen" @keydown="onKeydown">
|
|
89
79
|
<template #header="{ titleId }">
|
|
90
80
|
<span class="wp-confirm-modal__head" :class="`wp-confirm-modal__head--${tone}`">
|
|
91
81
|
<Icon :name="tone === 'danger' ? 'exclamation-triangle' : 'info-circle'" />
|
|
92
|
-
<h3 :id="titleId" class="wp-modal__title" data-test="confirm-title">{{ title }}</h3>
|
|
82
|
+
<h3 :id="titleId" class="wp-confirm-modal__title" data-test="confirm-title">{{ title }}</h3>
|
|
93
83
|
</span>
|
|
94
84
|
</template>
|
|
95
85
|
<div class="wp-confirm-modal__body">
|
|
@@ -123,7 +113,7 @@ onBeforeUnmount(() => window.removeEventListener("keydown", onKeydown));
|
|
|
123
113
|
}
|
|
124
114
|
.wp-confirm-modal__head--danger { color: var(--wp-danger); }
|
|
125
115
|
.wp-confirm-modal__head--warn { color: var(--wp-warn); }
|
|
126
|
-
.wp-confirm-
|
|
116
|
+
.wp-confirm-modal__title { margin: 0; font-size: var(--wp-text-md); font-weight: 600; color: var(--wp-text); }
|
|
127
117
|
.wp-confirm-modal__text {
|
|
128
118
|
margin: 0;
|
|
129
119
|
color: var(--wp-text-muted);
|
package/src/ContextMenu.vue
CHANGED
|
@@ -204,7 +204,7 @@ function isSectionEntry(entry: ContextMenuEntry): entry is ContextMenuSection {
|
|
|
204
204
|
<ul
|
|
205
205
|
v-if="visible"
|
|
206
206
|
ref="menuEl"
|
|
207
|
-
class="wp-ctxmenu"
|
|
207
|
+
class="wp-ctxmenu wp-menu"
|
|
208
208
|
:style="{ left: `${pos.x}px`, top: `${pos.y}px` }"
|
|
209
209
|
role="menu"
|
|
210
210
|
@click.stop
|
|
@@ -235,7 +235,8 @@ function isSectionEntry(entry: ContextMenuEntry): entry is ContextMenuSection {
|
|
|
235
235
|
can't see it); keys come from the menu's own keydown. -->
|
|
236
236
|
<!-- eslint-disable-next-line vuejs-accessibility/no-static-element-interactions, vuejs-accessibility/click-events-have-key-events, vuejs-accessibility/mouse-events-have-key-events -->
|
|
237
237
|
<li
|
|
238
|
-
class="wp-ctxmenu__item"
|
|
238
|
+
class="wp-ctxmenu__item wp-menu__item"
|
|
239
|
+
:data-active="activeIndex === i || undefined"
|
|
239
240
|
:class="{
|
|
240
241
|
'wp-ctxmenu__item--danger': entry.danger,
|
|
241
242
|
'wp-ctxmenu__item--accent': entry.accent,
|
|
@@ -268,26 +269,16 @@ function isSectionEntry(entry: ContextMenuEntry): entry is ContextMenuSection {
|
|
|
268
269
|
</Teleport>
|
|
269
270
|
</template>
|
|
270
271
|
|
|
272
|
+
<style scoped src="./menu.css"></style>
|
|
273
|
+
|
|
271
274
|
<style scoped>
|
|
275
|
+
/* The panel (background, border, corners, padding, shadow, layer, text) and
|
|
276
|
+
* the rows' padding, corners and hover / active / disabled states are the
|
|
277
|
+
* shared menu look (menu.css), the same as Select's and Combobox's lists. */
|
|
272
278
|
.wp-ctxmenu {
|
|
273
279
|
position: fixed;
|
|
274
|
-
|
|
275
|
-
background: var(--wp-bg-2);
|
|
276
|
-
border: 1px solid var(--wp-border-strong);
|
|
277
|
-
border-radius: var(--wp-radius-sm);
|
|
278
|
-
box-shadow: var(--wp-shadow-md);
|
|
279
|
-
/* Tighter padding than the old 4px-0 to match the v2 mockup. Items
|
|
280
|
-
* carry their own 6px padding + rounded corners so the menu reads
|
|
281
|
-
* as a stack of pills, not a flat list. */
|
|
282
|
-
padding: 4px;
|
|
283
|
-
/* Bumped from 180px to 250px — subtitles wrap awkwardly at narrower
|
|
284
|
-
* widths. Existing callers without subtitles still fit comfortably. */
|
|
280
|
+
/* Subtitles wrap awkwardly below 250px. */
|
|
285
281
|
min-width: 250px;
|
|
286
|
-
list-style: none;
|
|
287
|
-
margin: 0;
|
|
288
|
-
font-family: var(--wp-font-sans, sans-serif);
|
|
289
|
-
font-size: 12px;
|
|
290
|
-
color: var(--wp-text);
|
|
291
282
|
user-select: none;
|
|
292
283
|
}
|
|
293
284
|
/* Header — scope label at the top of the menu. Border-bottom acts as
|
|
@@ -319,21 +310,16 @@ function isSectionEntry(entry: ContextMenuEntry): entry is ContextMenuSection {
|
|
|
319
310
|
padding: 6px 8px 4px 8px;
|
|
320
311
|
}
|
|
321
312
|
.wp-ctxmenu__item {
|
|
322
|
-
padding: 6px 8px;
|
|
323
|
-
border-radius: min(4px, var(--wp-radius-cap, 999px));
|
|
324
|
-
cursor: pointer;
|
|
325
313
|
white-space: nowrap;
|
|
326
314
|
display: flex;
|
|
327
315
|
align-items: center;
|
|
328
316
|
gap: 8px;
|
|
329
317
|
}
|
|
330
|
-
.wp-ctxmenu__item--active,
|
|
331
|
-
.wp-ctxmenu__item:hover { background: var(--wp-accent-glow); color: var(--wp-accent); }
|
|
332
318
|
/* A checkable item's tick: always takes its room, shows only when on. */
|
|
333
319
|
.wp-ctxmenu__check { width: 14px; font-size: 11px; text-align: center; visibility: hidden; color: var(--wp-accent); }
|
|
334
320
|
.wp-ctxmenu__check[data-on] { visibility: visible; }
|
|
335
321
|
.wp-ctxmenu__item--danger { color: var(--wp-red); }
|
|
336
|
-
.wp-ctxmenu__item--danger
|
|
322
|
+
.wp-ctxmenu__item--danger[data-active="true"],
|
|
337
323
|
.wp-ctxmenu__item--danger:hover { background: var(--wp-red-bg); color: var(--wp-red); }
|
|
338
324
|
/* Accent variant — recommended action highlight. Tinted background +
|
|
339
325
|
* accent text in idle state so the eye lands on it; hover uses the
|
|
@@ -342,16 +328,11 @@ function isSectionEntry(entry: ContextMenuEntry): entry is ContextMenuSection {
|
|
|
342
328
|
color: var(--wp-accent);
|
|
343
329
|
background: color-mix(in oklab, var(--wp-accent) 12%, transparent);
|
|
344
330
|
}
|
|
345
|
-
.wp-ctxmenu__item--accent
|
|
331
|
+
.wp-ctxmenu__item--accent[data-active="true"],
|
|
346
332
|
.wp-ctxmenu__item--accent:hover {
|
|
347
333
|
background: var(--wp-accent-glow);
|
|
348
334
|
color: var(--wp-accent);
|
|
349
335
|
}
|
|
350
|
-
.wp-ctxmenu__item--disabled {
|
|
351
|
-
color: var(--wp-text-dim);
|
|
352
|
-
cursor: not-allowed;
|
|
353
|
-
}
|
|
354
|
-
.wp-ctxmenu__item--disabled:hover { background: transparent; color: var(--wp-text-dim); }
|
|
355
336
|
.wp-ctxmenu__icon,
|
|
356
337
|
.wp-ctxmenu__icon-spacer {
|
|
357
338
|
width: 14px;
|
package/src/CopyButton.vue
CHANGED
|
@@ -119,7 +119,7 @@ onBeforeUnmount(() => clearTimeout(timer));
|
|
|
119
119
|
.wp-copy-btn[data-size="lg"] { min-width: var(--wp-control-h-lg); height: var(--wp-control-h-lg); }
|
|
120
120
|
.wp-copy-btn[data-variant="secondary"] { background: var(--wp-bg-3); border-color: var(--wp-border); color: var(--wp-text); }
|
|
121
121
|
.wp-copy-btn:not([data-inline]) .pi { font-size: 12px; }
|
|
122
|
-
.wp-copy-btn[data-size="sm"]:not([data-inline]) .pi { font-size:
|
|
122
|
+
.wp-copy-btn[data-size="sm"]:not([data-inline]) .pi { font-size: 12px; }
|
|
123
123
|
.wp-copy-btn:not([data-inline]):hover:not(:disabled) { background: var(--wp-bg-3); border-color: var(--wp-border); color: var(--wp-text); }
|
|
124
124
|
.wp-copy-btn[data-variant="secondary"]:hover:not(:disabled) { background: var(--wp-bg-4); border-color: var(--wp-border-strong); }
|
|
125
125
|
.wp-copy-btn:not([data-inline]):focus-visible { outline: none; box-shadow: 0 0 0 2px var(--wp-border-focus); }
|
package/src/DataTable.vue
CHANGED
|
@@ -165,7 +165,6 @@ const span = computed(() => props.columns.length + (props.selectable ? 1 : 0));
|
|
|
165
165
|
<tr>
|
|
166
166
|
<th v-if="selectable" class="wp-data-table__check" scope="col">
|
|
167
167
|
<Checkbox
|
|
168
|
-
class="wp-checkbox--inline"
|
|
169
168
|
:model-value="allPicked"
|
|
170
169
|
:indeterminate="somePicked"
|
|
171
170
|
:disabled="loading || allKeys.length === 0"
|
|
@@ -227,7 +226,6 @@ const span = computed(() => props.columns.length + (props.selectable ? 1 : 0));
|
|
|
227
226
|
>
|
|
228
227
|
<td v-if="selectable" class="wp-data-table__check" @click.stop>
|
|
229
228
|
<Checkbox
|
|
230
|
-
class="wp-checkbox--inline"
|
|
231
229
|
:model-value="picked.has(keyOf(row))"
|
|
232
230
|
aria-label="Select row"
|
|
233
231
|
@update:model-value="(on: boolean) => toggleRow(row, on)"
|
package/src/DatePicker.vue
CHANGED
|
@@ -209,7 +209,7 @@ function clear(): void {
|
|
|
209
209
|
padding: 0 var(--wp-space-2); border: 0; border-radius: var(--wp-radius-sm);
|
|
210
210
|
background: transparent; color: var(--wp-text-muted); cursor: pointer; font: inherit;
|
|
211
211
|
}
|
|
212
|
-
.wp-date__nav .pi { font-size:
|
|
212
|
+
.wp-date__nav .pi { font-size: 12px; }
|
|
213
213
|
.wp-date__nav:hover, .wp-date__link:hover:not(:disabled) { background: var(--wp-row-hover); color: var(--wp-text); }
|
|
214
214
|
.wp-date__nav:focus-visible, .wp-date__link:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--wp-border-focus); }
|
|
215
215
|
.wp-date__link:disabled { opacity: 0.45; cursor: not-allowed; }
|
package/src/Disclosure.vue
CHANGED
|
@@ -120,7 +120,8 @@ function toggle(): void {
|
|
|
120
120
|
.wp-disclosure__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
121
121
|
.wp-disclosure__chev {
|
|
122
122
|
flex: none;
|
|
123
|
-
|
|
123
|
+
/* A whole even size: 0.75em came to 9px and sat half a pixel off. */
|
|
124
|
+
font-size: 10px;
|
|
124
125
|
color: var(--wp-text-muted);
|
|
125
126
|
transition: transform var(--wp-motion-swap) var(--wp-motion-curve-collapse);
|
|
126
127
|
}
|
package/src/Drawer.vue
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Tab stays inside it and focus goes back where it was. With `modal` off
|
|
6
6
|
* there is no backdrop, Tab moves on to the page and the page stays usable.
|
|
7
7
|
*/
|
|
8
|
-
import
|
|
8
|
+
import Button from "./Button.vue";
|
|
9
9
|
import { ref, useId, useSlots } from "vue";
|
|
10
10
|
import { useDialogFocus } from "./useDialogFocus";
|
|
11
11
|
|
|
@@ -66,7 +66,7 @@ useDialogFocus(panel, () => props.open, {
|
|
|
66
66
|
<h3 v-if="title" :id="titleId" class="wp-drawer__title">{{ title }}</h3>
|
|
67
67
|
<slot name="header" />
|
|
68
68
|
<span class="wp-spacer" />
|
|
69
|
-
<
|
|
69
|
+
<Button variant="ghost" icon="times" aria-label="Close" kbd="Esc" data-test="drawer-close" @click="close" />
|
|
70
70
|
</header>
|
|
71
71
|
<div class="wp-drawer__body">
|
|
72
72
|
<slot />
|
package/src/DropZone.vue
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { computed, ref } from "vue";
|
|
17
17
|
import { dragItemMaybeAccepted, fileAccepted, formatBytes } from "./drop-zone";
|
|
18
|
-
import
|
|
18
|
+
import Button from "./Button.vue";
|
|
19
19
|
|
|
20
20
|
export interface RejectedFile { file: File; reason: string }
|
|
21
21
|
|
|
@@ -152,7 +152,7 @@ defineExpose({ browse });
|
|
|
152
152
|
<span class="wp-drop__label">{{ over === "refuse" ? refuseWhy : mainLabel }}</span>
|
|
153
153
|
<span v-if="hint && over !== 'refuse'" class="wp-drop__hint">{{ hint }}{{ maxSize ? ` · up to ${formatBytes(maxSize)}` : "" }}</span>
|
|
154
154
|
</span>
|
|
155
|
-
<
|
|
155
|
+
<Button class="wp-drop__browse" variant="secondary" size="sm" icon="pi-folder-open" :disabled="disabled" @click="browse">{{ browseLabel }}</Button>
|
|
156
156
|
<input ref="inputEl" class="wp-drop__input" type="file" :accept="accept" :multiple="multiple" tabindex="-1" aria-hidden="true" @change="onPicked">
|
|
157
157
|
</div>
|
|
158
158
|
</template>
|
package/src/HsvPicker.vue
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* HsvPicker — saturation/value square + hue slider.
|
|
4
4
|
*
|
|
5
|
-
* Shared between
|
|
5
|
+
* Shared between ColorField / TweaksPanel AND the canvas
|
|
6
6
|
* bundle modal. CSS uses `--wp-*` tokens with hardcoded fallbacks so
|
|
7
7
|
* the picker renders inside the extension where the manager spacing
|
|
8
8
|
* tokens (`--wp-space-*`) aren't loaded.
|
|
@@ -167,7 +167,7 @@ const hueCursorStyle = computed(() => ({
|
|
|
167
167
|
position: absolute;
|
|
168
168
|
width: 14px;
|
|
169
169
|
height: 14px;
|
|
170
|
-
border-radius: 50
|
|
170
|
+
border-radius: min(50%, var(--wp-radius-cap, 999px));
|
|
171
171
|
border: 2px solid #fff;
|
|
172
172
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.45);
|
|
173
173
|
transform: translate(-50%, -50%);
|
package/src/IconPicker.vue
CHANGED
|
@@ -144,7 +144,7 @@ function pick(name: string | null): void {
|
|
|
144
144
|
line-height: 1;
|
|
145
145
|
}
|
|
146
146
|
.wp-iconpick__none { color: var(--wp-text-dim); line-height: 1; }
|
|
147
|
-
.wp-iconpick__caret { font-size:
|
|
147
|
+
.wp-iconpick__caret { font-size: 10px; color: var(--wp-text-dim); line-height: 1; }
|
|
148
148
|
</style>
|
|
149
149
|
|
|
150
150
|
<style>
|
package/src/ImageGrid.vue
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { computed, ref } from "vue";
|
|
16
16
|
import Lightbox from "./Lightbox.vue";
|
|
17
17
|
import Skeleton from "./Skeleton.vue";
|
|
18
|
-
import
|
|
18
|
+
import Checkbox from "./Checkbox.vue";
|
|
19
19
|
|
|
20
20
|
export interface GridImage { id: string; src: string; alt: string; thumb?: string; caption?: string }
|
|
21
21
|
|
|
@@ -82,7 +82,7 @@ function toggle(id: string, on: boolean): void {
|
|
|
82
82
|
<img v-if="img.thumb || img.src" class="wp-image-grid__img" :src="img.thumb || img.src" alt="" loading="lazy">
|
|
83
83
|
<Skeleton v-else class="wp-image-grid__empty" shape="block" width="100%" height="100%" />
|
|
84
84
|
</button>
|
|
85
|
-
<
|
|
85
|
+
<Checkbox
|
|
86
86
|
v-if="selectable"
|
|
87
87
|
class="wp-image-grid__check"
|
|
88
88
|
:model-value="props.selected.includes(img.id)"
|
package/src/Lightbox.vue
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { computed, nextTick, ref, watch } from "vue";
|
|
17
17
|
import ModalShell from "./ModalShell.vue";
|
|
18
18
|
import WpImageZoom from "./WpImageZoom.vue";
|
|
19
|
-
import
|
|
19
|
+
import Button from "./Button.vue";
|
|
20
20
|
import Skeleton from "./Skeleton.vue";
|
|
21
21
|
import { focusablesIn } from "./useDialogFocus";
|
|
22
22
|
|
|
@@ -104,7 +104,7 @@ watch(() => props.open, async (v, was) => {
|
|
|
104
104
|
>
|
|
105
105
|
<div class="wp-lightbox__bar">
|
|
106
106
|
<span v-if="count > 1" class="wp-lightbox__count" aria-hidden="true">{{ at + 1 }} / {{ count }}</span>
|
|
107
|
-
<
|
|
107
|
+
<Button variant="ghost" icon="times" aria-label="Close" class="wp-lightbox__close" kbd="Esc" @click="close" />
|
|
108
108
|
</div>
|
|
109
109
|
<figure v-if="image" class="wp-lightbox__figure" :data-zoom="zoom || undefined">
|
|
110
110
|
<!-- An image with no src yet (still rendering): its shape, not an empty frame. -->
|