@dumiflex/wp-kit 0.5.0 → 0.7.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 +47 -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/BundlePreview.vue +254 -0
- 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/CombinePreview.vue +150 -0
- package/src/Combobox.vue +10 -14
- package/src/ConfirmModal.vue +12 -22
- package/src/ConstraintMatrixPreview.vue +6 -22
- package/src/ConstraintPreview.vue +259 -0
- 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/DerivationCondition.vue +68 -0
- package/src/DerivationPreview.vue +345 -0
- package/src/Disclosure.vue +2 -1
- package/src/Drawer.vue +2 -2
- package/src/DropZone.vue +2 -2
- package/src/FixedValuesPreview.vue +184 -0
- package/src/HsvPicker.vue +2 -2
- package/src/IconPicker.vue +32 -103
- package/src/IconPickerPanel.vue +132 -0
- package/src/ImageGrid.vue +2 -2
- package/src/Lightbox.vue +2 -2
- package/src/LoadingMark.vue +6 -61
- package/src/Modal.vue +109 -44
- package/src/ModalShell.vue +26 -5
- package/src/Pagination.vue +4 -4
- package/src/PayloadPreview.vue +154 -0
- package/src/Popover.vue +68 -12
- package/src/PostDetailSkeleton.vue +65 -0
- package/src/PostGridSkeleton.vue +61 -0
- package/src/PreviewGroup.vue +75 -0
- package/src/ProfileSkeleton.vue +73 -0
- package/src/RadioGroup.vue +5 -4
- package/src/RichText.vue +61 -0
- package/src/SearchInput.vue +1 -1
- package/src/Select.vue +53 -158
- package/src/SelectionBar.vue +90 -0
- package/src/SettingGroup.vue +57 -0
- package/src/SettingRow.vue +127 -0
- 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/WildcardPreview.vue +607 -0
- package/src/WorkingLine.vue +234 -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/canvas-motion.ts +251 -0
- package/src/floating.ts +5 -0
- package/src/hsv.ts +1 -1
- package/src/kind-colour.ts +24 -1
- package/src/linkify.ts +142 -0
- package/src/loading-mark-motion.ts +12 -155
- package/src/matrix-axis.ts +28 -0
- package/src/menu.css +51 -0
- package/src/motion.css +73 -0
- package/src/pi-icons.ts +51 -0
- package/src/preview-readers.ts +401 -0
- package/src/preview.css +194 -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 +15 -4
- package/src/tooltip.css +8 -5
- package/src/useDialogFocus.ts +11 -2
- package/src/working-line-motion.ts +135 -0
- 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/RangeSlider.vue
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Range slider: a low and a high number on one track (a weight range, a
|
|
4
|
-
* token budget, a sweep's from–to). Two thumbs that can't cross, kept at
|
|
5
|
-
* least `min-gap` apart; `with-input` adds a WpNumber for each end. Works on
|
|
6
|
-
* the canvas (`size="sm"`) and in the manager (`md`).
|
|
7
|
-
*
|
|
8
|
-
* <RangeSlider v-model="range" :min="0" :max="2" :step="0.1" aria-label="Weight" with-input />
|
|
9
|
-
*
|
|
10
|
-
* Each thumb is an APG slider ("Weight, low end" / "high end") whose
|
|
11
|
-
* min and max are the other thumb, so screen readers hear the real limits.
|
|
12
|
-
* A click on the track moves the nearer thumb.
|
|
13
|
-
*/
|
|
14
|
-
import { computed, ref } from "vue";
|
|
15
|
-
import { keyStep, percentOf, snap, valueAt } from "./slider-math";
|
|
16
|
-
import WpNumber from "./WpNumber.vue";
|
|
17
|
-
|
|
18
|
-
const props = withDefaults(defineProps<{
|
|
19
|
-
modelValue: [number, number];
|
|
20
|
-
min?: number | undefined;
|
|
21
|
-
max?: number | undefined;
|
|
22
|
-
step?: number | undefined;
|
|
23
|
-
/** The smallest distance between the two ends. */
|
|
24
|
-
minGap?: number | undefined;
|
|
25
|
-
size?: "sm" | "md" | "lg" | undefined;
|
|
26
|
-
disabled?: boolean | undefined;
|
|
27
|
-
ariaLabel?: string | undefined;
|
|
28
|
-
format?: ((v: number) => string) | undefined;
|
|
29
|
-
withInput?: boolean | undefined;
|
|
30
|
-
}>(), { min: 0, max: 100, step: 1, minGap: 0, size: "md", ariaLabel: "Range" });
|
|
31
|
-
|
|
32
|
-
const emit = defineEmits<{ (e: "update:modelValue", v: [number, number]): void; (e: "change", v: [number, number]): void }>();
|
|
33
|
-
|
|
34
|
-
const trackEl = ref<HTMLElement | null>(null);
|
|
35
|
-
const active = ref<0 | 1 | null>(null);
|
|
36
|
-
|
|
37
|
-
const lo = computed(() => snap(Math.min(props.modelValue[0], props.modelValue[1]), props.min, props.max, props.step));
|
|
38
|
-
const hi = computed(() => snap(Math.max(props.modelValue[0], props.modelValue[1]), props.min, props.max, props.step));
|
|
39
|
-
const loPct = computed(() => percentOf(lo.value, props.min, props.max));
|
|
40
|
-
const hiPct = computed(() => percentOf(hi.value, props.min, props.max));
|
|
41
|
-
const text = (v: number): string => (props.format ? props.format(v) : String(v));
|
|
42
|
-
|
|
43
|
-
/** Put one end at `v`, kept inside the other end minus the gap. */
|
|
44
|
-
function setEnd(end: 0 | 1, v: number): [number, number] {
|
|
45
|
-
const next: [number, number] = end === 0
|
|
46
|
-
? [Math.min(v, snap(hi.value - props.minGap, props.min, props.max, props.step)), hi.value]
|
|
47
|
-
: [lo.value, Math.max(v, snap(lo.value + props.minGap, props.min, props.max, props.step))];
|
|
48
|
-
if (next[0] !== props.modelValue[0] || next[1] !== props.modelValue[1]) emit("update:modelValue", next);
|
|
49
|
-
return next;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function fromPointer(ev: PointerEvent): number {
|
|
53
|
-
const r = trackEl.value?.getBoundingClientRect();
|
|
54
|
-
return r ? valueAt(ev.clientX, r.left, r.width, props.min, props.max, props.step) : lo.value;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function onPointerDown(ev: PointerEvent): void {
|
|
58
|
-
if (props.disabled || ev.button !== 0) return;
|
|
59
|
-
ev.preventDefault();
|
|
60
|
-
const v = fromPointer(ev);
|
|
61
|
-
active.value = Math.abs(v - lo.value) <= Math.abs(v - hi.value) && !(v > hi.value) ? 0 : 1;
|
|
62
|
-
(ev.currentTarget as HTMLElement).setPointerCapture?.(ev.pointerId);
|
|
63
|
-
setEnd(active.value, v);
|
|
64
|
-
trackEl.value?.querySelectorAll<HTMLElement>("[role=slider]")[active.value]?.focus();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function onPointerMove(ev: PointerEvent): void {
|
|
68
|
-
if (active.value !== null) setEnd(active.value, fromPointer(ev));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function onPointerUp(ev: PointerEvent): void {
|
|
72
|
-
if (active.value === null) return;
|
|
73
|
-
active.value = null;
|
|
74
|
-
(ev.currentTarget as HTMLElement).releasePointerCapture?.(ev.pointerId);
|
|
75
|
-
emit("change", [lo.value, hi.value]);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function onKeydown(ev: KeyboardEvent, end: 0 | 1): void {
|
|
79
|
-
if (props.disabled) return;
|
|
80
|
-
const lowLimit = end === 0 ? props.min : lo.value + props.minGap;
|
|
81
|
-
const highLimit = end === 0 ? hi.value - props.minGap : props.max;
|
|
82
|
-
const cur = end === 0 ? lo.value : hi.value;
|
|
83
|
-
const next = keyStep(ev.key, cur, props.min, props.max, props.step);
|
|
84
|
-
if (next === null) return;
|
|
85
|
-
ev.preventDefault();
|
|
86
|
-
const v = ev.key === "Home" ? lowLimit : ev.key === "End" ? highLimit : next;
|
|
87
|
-
emit("change", setEnd(end, v));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function onTyped(end: 0 | 1, v: number | null): void {
|
|
91
|
-
if (v === null) return;
|
|
92
|
-
emit("change", setEnd(end, snap(v, props.min, props.max, props.step)));
|
|
93
|
-
}
|
|
94
|
-
</script>
|
|
95
|
-
|
|
96
|
-
<template>
|
|
97
|
-
<div class="wp-range-slider" :data-size="size" :data-disabled="disabled || undefined">
|
|
98
|
-
<WpNumber
|
|
99
|
-
v-if="withInput"
|
|
100
|
-
class="wp-range-slider__input"
|
|
101
|
-
:model-value="lo"
|
|
102
|
-
:min="min"
|
|
103
|
-
:max="hi - props.minGap"
|
|
104
|
-
:step="step"
|
|
105
|
-
:size="size"
|
|
106
|
-
:disabled="disabled"
|
|
107
|
-
:aria-label="`${ariaLabel}, low end`"
|
|
108
|
-
@update:model-value="onTyped(0, $event)"
|
|
109
|
-
/>
|
|
110
|
-
<div
|
|
111
|
-
ref="trackEl"
|
|
112
|
-
class="wp-range-slider__track"
|
|
113
|
-
@pointerdown="onPointerDown"
|
|
114
|
-
@pointermove="onPointerMove"
|
|
115
|
-
@pointerup="onPointerUp"
|
|
116
|
-
@pointercancel="onPointerUp"
|
|
117
|
-
>
|
|
118
|
-
<div class="wp-range-slider__rail" />
|
|
119
|
-
<div class="wp-range-slider__fill" :style="{ left: `${loPct}%`, width: `${hiPct - loPct}%` }" />
|
|
120
|
-
<!-- Focusable unless disabled (tabindex is bound). -->
|
|
121
|
-
<!-- eslint-disable-next-line vuejs-accessibility/interactive-supports-focus -->
|
|
122
|
-
<div
|
|
123
|
-
v-for="end in ([0, 1] as const)"
|
|
124
|
-
:key="end"
|
|
125
|
-
class="wp-range-slider__thumb"
|
|
126
|
-
role="slider"
|
|
127
|
-
:tabindex="disabled ? -1 : 0"
|
|
128
|
-
:aria-label="`${ariaLabel}, ${end === 0 ? 'low' : 'high'} end`"
|
|
129
|
-
:aria-valuemin="end === 0 ? min : lo + props.minGap"
|
|
130
|
-
:aria-valuemax="end === 0 ? hi - props.minGap : max"
|
|
131
|
-
:aria-valuenow="end === 0 ? lo : hi"
|
|
132
|
-
:aria-valuetext="format ? text(end === 0 ? lo : hi) : undefined"
|
|
133
|
-
:aria-disabled="disabled || undefined"
|
|
134
|
-
:data-active="active === end || undefined"
|
|
135
|
-
:style="{ left: `${end === 0 ? loPct : hiPct}%` }"
|
|
136
|
-
@keydown="onKeydown($event, end)"
|
|
137
|
-
/>
|
|
138
|
-
</div>
|
|
139
|
-
<WpNumber
|
|
140
|
-
v-if="withInput"
|
|
141
|
-
class="wp-range-slider__input"
|
|
142
|
-
:model-value="hi"
|
|
143
|
-
:min="lo + props.minGap"
|
|
144
|
-
:max="max"
|
|
145
|
-
:step="step"
|
|
146
|
-
:size="size"
|
|
147
|
-
:disabled="disabled"
|
|
148
|
-
:aria-label="`${ariaLabel}, high end`"
|
|
149
|
-
@update:model-value="onTyped(1, $event)"
|
|
150
|
-
/>
|
|
151
|
-
<span v-else class="wp-range-slider__value" aria-hidden="true">{{ text(lo) }} – {{ text(hi) }}</span>
|
|
152
|
-
</div>
|
|
153
|
-
</template>
|
|
154
|
-
|
|
155
|
-
<style scoped>
|
|
156
|
-
.wp-range-slider {
|
|
157
|
-
--_h: var(--wp-control-h-md); --_thumb: 16px;
|
|
158
|
-
display: flex; align-items: center; gap: var(--wp-space-4); min-width: 0; width: 100%; min-height: var(--_h);
|
|
159
|
-
font-family: var(--wp-font-sans);
|
|
160
|
-
}
|
|
161
|
-
.wp-range-slider[data-size="sm"] { --_h: var(--wp-control-h-sm); --_thumb: 12px; gap: var(--wp-space-3); }
|
|
162
|
-
.wp-range-slider[data-size="lg"] { --_h: var(--wp-control-h-lg); --_thumb: 18px; }
|
|
163
|
-
.wp-range-slider__track { position: relative; flex: 1; min-width: 80px; height: var(--_h); margin: 0 calc(var(--_thumb) / 2); cursor: pointer; touch-action: none; }
|
|
164
|
-
.wp-range-slider__rail, .wp-range-slider__fill { position: absolute; top: 50%; height: 4px; margin-top: -2px; border-radius: var(--wp-radius-cap, 999px); }
|
|
165
|
-
.wp-range-slider__rail { left: calc(var(--_thumb) / -2); right: calc(var(--_thumb) / -2); background: var(--wp-bg-3); box-shadow: inset 0 0 0 1px var(--wp-border-soft); }
|
|
166
|
-
.wp-range-slider__fill { background: var(--wp-accent); }
|
|
167
|
-
.wp-range-slider[data-size="sm"] .wp-range-slider__rail, .wp-range-slider[data-size="sm"] .wp-range-slider__fill { height: 3px; margin-top: -1.5px; }
|
|
168
|
-
.wp-range-slider__thumb {
|
|
169
|
-
position: absolute; top: 50%; box-sizing: border-box; width: var(--_thumb); height: var(--_thumb);
|
|
170
|
-
margin: calc(var(--_thumb) / -2) 0 0 calc(var(--_thumb) / -2);
|
|
171
|
-
border: 2px solid var(--wp-accent); border-radius: min(50%, var(--wp-radius-cap, 999px)); background: var(--wp-bg); box-shadow: var(--wp-shadow-sm); outline: none;
|
|
172
|
-
transition: transform var(--wp-motion-quick), box-shadow var(--wp-motion-quick);
|
|
173
|
-
}
|
|
174
|
-
.wp-range-slider__thumb[data-active], .wp-range-slider__thumb:hover { transform: scale(1.12); }
|
|
175
|
-
.wp-range-slider__thumb:focus-visible { box-shadow: var(--wp-focus-ring); z-index: 1; }
|
|
176
|
-
.wp-range-slider__value { flex: none; color: var(--wp-text); font: 500 var(--wp-text-sm)/1 var(--wp-font-sans); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
177
|
-
.wp-range-slider[data-size="sm"] .wp-range-slider__value { font-size: var(--wp-text-xs); }
|
|
178
|
-
.wp-range-slider__input { flex: none; width: 76px; }
|
|
179
|
-
.wp-range-slider[data-size="sm"] .wp-range-slider__input { width: 60px; }
|
|
180
|
-
.wp-range-slider[data-disabled] { opacity: 0.45; }
|
|
181
|
-
.wp-range-slider[data-disabled] .wp-range-slider__track { cursor: not-allowed; }
|
|
182
|
-
@media (prefers-reduced-motion: reduce) { .wp-range-slider__thumb { transition: none; } }
|
|
183
|
-
</style>
|
package/src/Toggle.vue
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* The manager's on/off switch: the shared WpSwitch at the
|
|
4
|
-
* manager size, so the canvas and the manager draw one switch. Use it for a
|
|
5
|
-
* setting that takes effect at once.
|
|
6
|
-
*/
|
|
7
|
-
import WpSwitch from "./WpSwitch.vue";
|
|
8
|
-
|
|
9
|
-
interface Props {
|
|
10
|
-
modelValue: boolean;
|
|
11
|
-
label?: string | undefined;
|
|
12
|
-
size?: "sm" | "md" | "lg" | undefined;
|
|
13
|
-
disabled?: boolean | undefined;
|
|
14
|
-
invalid?: boolean | undefined;
|
|
15
|
-
/** @deprecated use `invalid`. */
|
|
16
|
-
error?: boolean | undefined;
|
|
17
|
-
ariaLabel?: string | undefined;
|
|
18
|
-
}
|
|
19
|
-
const props = withDefaults(defineProps<Props>(), { size: "md", label: undefined, ariaLabel: undefined });
|
|
20
|
-
|
|
21
|
-
const emit = defineEmits<{
|
|
22
|
-
(e: "update:modelValue", v: boolean): void;
|
|
23
|
-
}>();
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<template>
|
|
27
|
-
<WpSwitch
|
|
28
|
-
:model-value="props.modelValue"
|
|
29
|
-
:size="props.size"
|
|
30
|
-
:label="props.label"
|
|
31
|
-
:aria-label="props.ariaLabel ?? props.label"
|
|
32
|
-
:disabled="props.disabled"
|
|
33
|
-
:invalid="props.invalid || props.error"
|
|
34
|
-
@update:model-value="emit('update:modelValue', $event)"
|
|
35
|
-
/>
|
|
36
|
-
</template>
|
package/src/WpButton.vue
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* The canvas twin of the manager Button (manager/components/ui/Button.vue).
|
|
4
|
-
* Same variants, sizes and states, same stylesheet (`button.css`), but the
|
|
5
|
-
* icon is a PrimeIcons class, as everywhere on the canvas, and the default
|
|
6
|
-
* size is the canvas `sm`.
|
|
7
|
-
*
|
|
8
|
-
* <WpButton variant="primary" icon="pi-plus">Add module</WpButton>
|
|
9
|
-
* <WpButton variant="ghost" icon="pi-download" aria-label="Download snapshot" />
|
|
10
|
-
* <WpButton variant="add" icon="pi-plus" block>Add rule</WpButton>
|
|
11
|
-
*
|
|
12
|
-
* With no slot content it is an icon button (`.wp-btn--icon`); its
|
|
13
|
-
* `aria-label` is also its tooltip (`hint` sets another, `kbd` adds a key
|
|
14
|
-
* chip). `pressed` turns it into a toggle (a "favourites only" star, a
|
|
15
|
-
* filter panel trigger). Loading swaps the icon for the sparkle, sets
|
|
16
|
-
* aria-busy and ignores clicks.
|
|
17
|
-
*
|
|
18
|
-
* The stylesheet is pulled in scoped, so the canvas never carries the
|
|
19
|
-
* `.wp-btn` classes globally. Row icon buttons are WpIconButton.
|
|
20
|
-
*/
|
|
21
|
-
import { computed, useSlots } from "vue";
|
|
22
|
-
import LoadingMark from "./LoadingMark.vue";
|
|
23
|
-
import { vTooltip } from "./tooltip";
|
|
24
|
-
|
|
25
|
-
const props = withDefaults(defineProps<{
|
|
26
|
-
variant?: "primary" | "secondary" | "ghost" | "danger" | "link" | "outline" | "add" | undefined;
|
|
27
|
-
size?: "sm" | "md" | "lg" | undefined;
|
|
28
|
-
/** PrimeIcons class (e.g. `pi-plus`), drawn before the label. */
|
|
29
|
-
icon?: string | undefined;
|
|
30
|
-
loading?: boolean | undefined;
|
|
31
|
-
disabled?: boolean | undefined;
|
|
32
|
-
/** Stretch to the container's full width. */
|
|
33
|
-
block?: boolean | undefined;
|
|
34
|
-
/** Makes it a toggle (`aria-pressed`): on, it wears the accent tint. */
|
|
35
|
-
pressed?: boolean | undefined;
|
|
36
|
-
type?: "button" | "submit" | "reset" | undefined;
|
|
37
|
-
ariaLabel?: string | undefined;
|
|
38
|
-
/** Tooltip text; an icon button shows its aria-label when this is unset.
|
|
39
|
-
* Use this, not `v-tooltip` on the component, which would fight the
|
|
40
|
-
* button's own. */
|
|
41
|
-
hint?: string | undefined;
|
|
42
|
-
/** A shortcut shown in the tooltip's key chip, e.g. "P". */
|
|
43
|
-
kbd?: string | undefined;
|
|
44
|
-
}>(), { variant: "secondary", size: "sm", icon: undefined, type: "button", ariaLabel: undefined, hint: undefined, kbd: undefined, pressed: undefined });
|
|
45
|
-
|
|
46
|
-
const emit = defineEmits<{ click: [evt: MouseEvent] }>();
|
|
47
|
-
|
|
48
|
-
const slots = useSlots();
|
|
49
|
-
const iconOnly = computed(() => !slots.default);
|
|
50
|
-
|
|
51
|
-
function onClick(e: MouseEvent): void {
|
|
52
|
-
if (props.disabled || props.loading) return;
|
|
53
|
-
emit("click", e);
|
|
54
|
-
}
|
|
55
|
-
</script>
|
|
56
|
-
|
|
57
|
-
<template>
|
|
58
|
-
<button
|
|
59
|
-
v-tooltip="{ text: hint ?? (iconOnly ? ariaLabel : undefined), kbd }"
|
|
60
|
-
:type="type"
|
|
61
|
-
class="wp-btn"
|
|
62
|
-
:class="[`wp-btn--${variant}`, `wp-btn--${size}`, { 'wp-btn--icon': iconOnly, 'wp-btn--block': block }]"
|
|
63
|
-
:disabled="disabled || loading"
|
|
64
|
-
:aria-label="ariaLabel"
|
|
65
|
-
:aria-busy="loading || undefined"
|
|
66
|
-
:aria-pressed="pressed === undefined ? undefined : pressed"
|
|
67
|
-
@click="onClick"
|
|
68
|
-
>
|
|
69
|
-
<LoadingMark v-if="loading" variant="sparkle" mono :size="size === 'lg' ? 16 : size === 'md' ? 14 : 12" decorative />
|
|
70
|
-
<i v-else-if="icon" class="pi" :class="icon" aria-hidden="true" />
|
|
71
|
-
<slot />
|
|
72
|
-
</button>
|
|
73
|
-
</template>
|
|
74
|
-
|
|
75
|
-
<style scoped src="./button.css"></style>
|
package/src/WpCheck.vue
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Shared checkbox box — the wildcard pool's per-option look (`.opt__check`
|
|
4
|
-
* in OptionRow.vue), extracted so every checkmark box in the *embed/canvas*
|
|
5
|
-
* tree reads the same.
|
|
6
|
-
*
|
|
7
|
-
* Why a component (not the global `.wp-check` class): the canvas bundle only
|
|
8
|
-
* loads a hand-picked CSS set via `src/extension/settings.ts` — it does NOT
|
|
9
|
-
* load the SPA's `manager/styles/tokens.css`, where global `.wp-check` lives.
|
|
10
|
-
* A component's `<style scoped>` is compiled into the component's own chunk,
|
|
11
|
-
* so it ships wherever the component renders — SPA *and* canvas. That's the
|
|
12
|
-
* same reason `.opt__check` / `.row__check` / `.ex__check` are each scoped.
|
|
13
|
-
*
|
|
14
|
-
* Custom toggle BUTTONS (internal flag, seed-lock) stay as their own styled
|
|
15
|
-
* buttons — this is only for true checkmark boxes.
|
|
16
|
-
*
|
|
17
|
-
* `aria-label` / `data-test` fall through to the single root span. Click /
|
|
18
|
-
* Space / Enter toggle. `size="sm"` is the 12px box of the canvas rows' on/off
|
|
19
|
-
* mark (modules, bundles, injector rows). A parent can tint the checked fill
|
|
20
|
-
* with `--check-on` (a bundle header uses its bundle colour).
|
|
21
|
-
*/
|
|
22
|
-
const props = withDefaults(
|
|
23
|
-
defineProps<{
|
|
24
|
-
/** Checked state — use with `v-model` or `:model-value` + `@update:model-value`. */
|
|
25
|
-
modelValue?: boolean | undefined;
|
|
26
|
-
disabled?: boolean | undefined;
|
|
27
|
-
size?: "sm" | "md" | undefined;
|
|
28
|
-
}>(),
|
|
29
|
-
{ modelValue: false, disabled: false, size: "md" },
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const emit = defineEmits<{ "update:modelValue": [next: boolean] }>();
|
|
33
|
-
|
|
34
|
-
function toggle(): void {
|
|
35
|
-
if (props.disabled) return;
|
|
36
|
-
emit("update:modelValue", !props.modelValue);
|
|
37
|
-
}
|
|
38
|
-
</script>
|
|
39
|
-
|
|
40
|
-
<template>
|
|
41
|
-
<!-- Focusable: tabindex is bound (-1 while disabled), which the rule can't see. -->
|
|
42
|
-
<!-- eslint-disable-next-line vuejs-accessibility/interactive-supports-focus -->
|
|
43
|
-
<span
|
|
44
|
-
class="wp-check"
|
|
45
|
-
:class="{ 'wp-check--sm': size === 'sm' }"
|
|
46
|
-
role="checkbox"
|
|
47
|
-
:aria-checked="modelValue === true"
|
|
48
|
-
:aria-disabled="disabled || undefined"
|
|
49
|
-
:tabindex="disabled ? -1 : 0"
|
|
50
|
-
@click="toggle"
|
|
51
|
-
@keydown.space.prevent="toggle"
|
|
52
|
-
@keydown.enter.prevent="toggle"
|
|
53
|
-
>
|
|
54
|
-
<!-- 8×8 SVG tick inside the 14px box — identical to OptionRow's
|
|
55
|
-
`.opt__check` so the canvas boxes match the wildcard options. -->
|
|
56
|
-
<svg
|
|
57
|
-
v-if="modelValue"
|
|
58
|
-
width="8"
|
|
59
|
-
height="8"
|
|
60
|
-
viewBox="0 0 12 12"
|
|
61
|
-
aria-hidden="true"
|
|
62
|
-
>
|
|
63
|
-
<path
|
|
64
|
-
d="M2.5 6.5 L5 9 L9.5 3.5"
|
|
65
|
-
fill="none"
|
|
66
|
-
stroke="currentColor"
|
|
67
|
-
stroke-width="2"
|
|
68
|
-
stroke-linecap="round"
|
|
69
|
-
stroke-linejoin="round"
|
|
70
|
-
/>
|
|
71
|
-
</svg>
|
|
72
|
-
</span>
|
|
73
|
-
</template>
|
|
74
|
-
|
|
75
|
-
<style scoped>
|
|
76
|
-
/* Verbatim from OptionRow.vue `.opt__check`. Scoped so it ships into the
|
|
77
|
-
* canvas bundle (no global tokens.css there). Vars resolve from the SPA's
|
|
78
|
-
* tokens.css or the canvas theme.css — both define these. */
|
|
79
|
-
.wp-check {
|
|
80
|
-
width: 14px;
|
|
81
|
-
height: 14px;
|
|
82
|
-
border: 1.5px solid var(--wp-border-soft, var(--wp-border));
|
|
83
|
-
border-radius: min(3px, var(--wp-radius-cap, 999px));
|
|
84
|
-
display: inline-flex;
|
|
85
|
-
align-items: center;
|
|
86
|
-
justify-content: center;
|
|
87
|
-
color: white;
|
|
88
|
-
background: var(--wp-bg);
|
|
89
|
-
flex-shrink: 0;
|
|
90
|
-
cursor: pointer;
|
|
91
|
-
}
|
|
92
|
-
.wp-check svg { display: block; }
|
|
93
|
-
.wp-check--sm { width: 12px; height: 12px; border-width: 1px; border-radius: min(2px, var(--wp-radius-cap, 999px)); }
|
|
94
|
-
.wp-check--sm svg { width: 7px; height: 7px; }
|
|
95
|
-
.wp-check:focus-visible { outline: none; box-shadow: var(--wp-focus-ring); }
|
|
96
|
-
.wp-check[aria-checked="true"] {
|
|
97
|
-
background: var(--check-on, var(--wp-accent));
|
|
98
|
-
border-color: var(--check-on, var(--wp-accent));
|
|
99
|
-
}
|
|
100
|
-
.wp-check[aria-disabled="true"] {
|
|
101
|
-
cursor: not-allowed;
|
|
102
|
-
opacity: 0.45;
|
|
103
|
-
}
|
|
104
|
-
</style>
|
package/src/_skeleton.css
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/* Skeleton loading frames.
|
|
2
|
-
*
|
|
3
|
-
* Used by modals + widgets that surface async API loads (bundle picker,
|
|
4
|
-
* module picker). The flat base background is token-driven so the
|
|
5
|
-
* frames track theme changes; a moving highlight gradient overlays
|
|
6
|
-
* via the `::after` pseudo so consumers don't need to mark up two
|
|
7
|
-
* elements. Animation loops on `--wp-motion-shimmer` (1400ms).
|
|
8
|
-
*
|
|
9
|
-
* Imported by each consumer's scoped style block — the `wp-skel`
|
|
10
|
-
* class names are intentionally global so a parent's scoped wrapper
|
|
11
|
-
* (`wp-bp__skel-row` etc.) can lay out the shapes while the base
|
|
12
|
-
* `wp-skel` rules drive the look. `prefers-reduced-motion` kills the
|
|
13
|
-
* sweep so reduced-motion users still get a flat placeholder.
|
|
14
|
-
*/
|
|
15
|
-
.wp-skel {
|
|
16
|
-
background: var(--wp-bg2);
|
|
17
|
-
border-radius: min(4px, var(--wp-radius-cap, 999px));
|
|
18
|
-
position: relative;
|
|
19
|
-
overflow: hidden;
|
|
20
|
-
}
|
|
21
|
-
.wp-skel::after {
|
|
22
|
-
content: "";
|
|
23
|
-
position: absolute;
|
|
24
|
-
inset: 0;
|
|
25
|
-
background: linear-gradient(
|
|
26
|
-
90deg,
|
|
27
|
-
transparent 0%,
|
|
28
|
-
color-mix(in srgb, var(--wp-text) 8%, transparent) 50%,
|
|
29
|
-
transparent 100%
|
|
30
|
-
);
|
|
31
|
-
transform: translateX(-100%);
|
|
32
|
-
animation: wp-skel-shimmer var(--wp-motion-shimmer) ease-in-out infinite;
|
|
33
|
-
}
|
|
34
|
-
.wp-skel--text {
|
|
35
|
-
height: 11px;
|
|
36
|
-
}
|
|
37
|
-
.wp-skel--circle {
|
|
38
|
-
border-radius: 50%; /* round: stands in for an avatar */
|
|
39
|
-
}
|
|
40
|
-
.wp-skel--pill {
|
|
41
|
-
border-radius: var(--wp-radius-cap, 999px);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@keyframes wp-skel-shimmer {
|
|
45
|
-
100% { transform: translateX(100%); }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@media (prefers-reduced-motion: reduce) {
|
|
49
|
-
.wp-skel::after { animation: none; }
|
|
50
|
-
}
|