@dumiflex/wp-kit 0.3.0 → 0.5.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 +92 -51
- package/LICENSE +674 -674
- package/README.md +73 -76
- package/package.json +78 -77
- package/src/Avatar.vue +83 -83
- package/src/Badge.vue +97 -97
- package/src/Banner.vue +118 -118
- package/src/BookmarkButton.vue +27 -27
- package/src/BundleMeta.vue +51 -51
- package/src/Button.vue +103 -103
- package/src/Card.vue +68 -68
- package/src/Checkbox.vue +109 -107
- package/src/Chip.vue +163 -97
- package/src/ChoiceCards.vue +133 -133
- package/src/CloseButton.vue +69 -69
- package/src/CodeBlock.vue +146 -119
- package/src/ColorField.vue +188 -206
- package/src/ColorPicker.vue +201 -201
- package/src/Combobox.vue +331 -334
- package/src/ConfirmDialog.vue +165 -163
- package/src/ConfirmModal.vue +147 -147
- package/src/ConstraintMatrixPreview.vue +708 -708
- package/src/ContextMenu.vue +386 -379
- package/src/CopyButton.vue +156 -156
- package/src/DataTable.vue +293 -292
- package/src/DatePicker.vue +234 -234
- package/src/DescriptionList.vue +73 -73
- package/src/DiffView.vue +68 -68
- package/src/Disclosure.vue +166 -166
- package/src/DragHandle.vue +49 -49
- package/src/Drawer.vue +131 -131
- package/src/DropZone.vue +196 -196
- package/src/DropdownMenu.vue +72 -72
- package/src/EmptyState.vue +111 -111
- package/src/Field.vue +28 -28
- package/src/HsvPicker.vue +208 -208
- package/src/Icon.vue +39 -39
- package/src/IconPicker.vue +209 -209
- package/src/ImageGrid.vue +127 -125
- package/src/InlineEdit.vue +183 -183
- package/src/Input.vue +99 -99
- package/src/Kbd.vue +56 -56
- package/src/Lightbox.vue +176 -173
- package/src/ListFilter.vue +85 -85
- package/src/ListRow.vue +204 -204
- package/src/LoadingMark.vue +217 -161
- package/src/Logo.vue +60 -60
- package/src/Modal.vue +78 -76
- package/src/ModalShell.vue +109 -107
- package/src/Pagination.vue +106 -106
- package/src/Popover.vue +180 -171
- package/src/PostCard.vue +210 -211
- package/src/ProgressBar.vue +90 -90
- package/src/RadioGroup.vue +177 -177
- package/src/RangeSlider.vue +183 -183
- package/src/RelativeDate.vue +35 -35
- package/src/ResizeHandle.vue +143 -143
- package/src/SearchInput.vue +192 -199
- package/src/Select.vue +644 -641
- package/src/Separator.vue +54 -54
- package/src/Skeleton.vue +98 -82
- package/src/Slider.vue +198 -198
- package/src/SortableList.vue +260 -260
- package/src/SplitPane.vue +68 -68
- package/src/StarButton.vue +40 -40
- package/src/Stepper.vue +141 -141
- package/src/SupportLinks.vue +93 -93
- package/src/TagInput.vue +297 -300
- package/src/Textarea.vue +57 -57
- package/src/Timeline.vue +92 -92
- package/src/ToastCard.vue +119 -119
- package/src/ToastHost.vue +40 -40
- package/src/Toggle.vue +36 -36
- package/src/TokenizedValue.vue +310 -310
- package/src/Toolbar.vue +110 -110
- package/src/VirtualList.vue +87 -87
- package/src/VisuallyHidden.vue +29 -29
- package/src/WpButton.vue +75 -75
- package/src/WpCheck.vue +104 -102
- package/src/WpIconButton.vue +107 -107
- package/src/WpImageZoom.vue +544 -544
- package/src/WpNumber.vue +178 -183
- package/src/WpSegmented.vue +175 -175
- package/src/WpSelect.vue +93 -89
- package/src/WpSwitch.vue +163 -163
- package/src/WpTabs.vue +188 -188
- package/src/_modal-motion.css +33 -33
- package/src/_skeleton.css +50 -50
- package/src/axis-color.ts +42 -42
- package/src/button.css +169 -169
- package/src/calendar.ts +59 -59
- package/src/clipboard.ts +50 -50
- package/src/colour-presets.ts +12 -12
- package/src/diff.ts +51 -51
- package/src/drop-zone.ts +41 -41
- package/src/field.css +50 -0
- package/src/flip.ts +237 -237
- package/src/floating-layer.ts +48 -0
- package/src/floating.ts +99 -99
- package/src/highlight.ts +199 -0
- package/src/hsv.ts +100 -100
- package/src/icon-catalog.ts +52 -52
- package/src/image-zoom.ts +93 -93
- package/src/kind-colour.ts +28 -0
- package/src/loading-mark-motion.ts +249 -0
- package/src/logo-paths.ts +21 -21
- package/src/matrix-axis.ts +91 -91
- package/src/pagination.ts +27 -27
- package/src/platform-keys.ts +29 -29
- package/src/rich-tokenize.ts +357 -357
- package/src/select-types.ts +32 -32
- package/src/slider-math.ts +45 -45
- package/src/sortable.ts +22 -22
- package/src/theme/fonts/OFL-Fraunces.txt +93 -93
- package/src/theme/fonts/OFL-JetBrainsMono.txt +93 -93
- package/src/theme/fonts/OFL-Manrope.txt +93 -93
- package/src/theme/looks.css +428 -428
- package/src/theme/palettes.css +254 -254
- package/src/theme/tokens.css +2896 -2894
- package/src/tokens.ts +56 -56
- package/src/tooltip.css +43 -43
- package/src/tooltip.ts +146 -146
- package/src/useDialogFocus.ts +73 -73
- package/src/useGrowableField.ts +357 -357
- package/src/useModalKeyShield.ts +116 -116
- package/src/useToast.ts +48 -48
- package/src/wp-logo.svg +14 -14
- package/src/ChipToggle.vue +0 -92
- package/src/KindChip.vue +0 -53
- package/src/RatingBadge.vue +0 -42
package/src/Popover.vue
CHANGED
|
@@ -1,171 +1,180 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* A panel anchored to a trigger: opens below it (or wherever `placement`
|
|
4
|
-
* says), flips and stays on screen, follows the trigger on scroll and
|
|
5
|
-
* resize, and closes on Escape or a click outside, giving focus back to
|
|
6
|
-
* the trigger. Works on the canvas and in the manager. Anything that
|
|
7
|
-
* floats next to a control builds on this instead of its own Teleport.
|
|
8
|
-
*
|
|
9
|
-
* <Popover v-model:open="open" label="Category colour">
|
|
10
|
-
* <template #trigger="{ props }"><Button v-bind="props">Colour</Button></template>
|
|
11
|
-
* …panel content…
|
|
12
|
-
* </Popover>
|
|
13
|
-
*
|
|
14
|
-
* One panel shared by many triggers (the cells of a rule grid) passes the
|
|
15
|
-
* element it belongs to as `anchor` instead of a trigger slot; clicks on that
|
|
16
|
-
* element don't count as outside, so it can toggle the panel itself.
|
|
17
|
-
*
|
|
18
|
-
* <Popover :open="!!cell" :anchor="cell?.el" label="Cell rule" @update:open="cell = null">…</Popover>
|
|
19
|
-
*/
|
|
20
|
-
import { computed, nextTick, onBeforeUnmount, ref, useId, watch } from "vue";
|
|
21
|
-
import { placeFloating, viewportSize, type Placement, type Side } from "./floating";
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"aria-
|
|
61
|
-
"aria-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
setOpen(false);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
place
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* A panel anchored to a trigger: opens below it (or wherever `placement`
|
|
4
|
+
* says), flips and stays on screen, follows the trigger on scroll and
|
|
5
|
+
* resize, and closes on Escape or a click outside, giving focus back to
|
|
6
|
+
* the trigger. Works on the canvas and in the manager. Anything that
|
|
7
|
+
* floats next to a control builds on this instead of its own Teleport.
|
|
8
|
+
*
|
|
9
|
+
* <Popover v-model:open="open" label="Category colour">
|
|
10
|
+
* <template #trigger="{ props }"><Button v-bind="props">Colour</Button></template>
|
|
11
|
+
* …panel content…
|
|
12
|
+
* </Popover>
|
|
13
|
+
*
|
|
14
|
+
* One panel shared by many triggers (the cells of a rule grid) passes the
|
|
15
|
+
* element it belongs to as `anchor` instead of a trigger slot; clicks on that
|
|
16
|
+
* element don't count as outside, so it can toggle the panel itself.
|
|
17
|
+
*
|
|
18
|
+
* <Popover :open="!!cell" :anchor="cell?.el" label="Cell rule" @update:open="cell = null">…</Popover>
|
|
19
|
+
*/
|
|
20
|
+
import { computed, nextTick, onBeforeUnmount, ref, useId, watch } from "vue";
|
|
21
|
+
import { placeFloating, viewportSize, type Placement, type Side } from "./floating";
|
|
22
|
+
import { provideFloatingLayer, useFloatingChild } from "./floating-layer";
|
|
23
|
+
|
|
24
|
+
const props = withDefaults(defineProps<{
|
|
25
|
+
open: boolean;
|
|
26
|
+
placement?: Placement | undefined;
|
|
27
|
+
/** The panel's name for screen readers. */
|
|
28
|
+
label?: string | undefined;
|
|
29
|
+
/** Make the panel at least as wide as the trigger (lists under a field). */
|
|
30
|
+
matchWidth?: boolean | undefined;
|
|
31
|
+
/** Move focus into the panel when it opens. Off for panels you type past. */
|
|
32
|
+
autofocus?: boolean | undefined;
|
|
33
|
+
/** "dialog" for panels with controls, "menu"/"listbox" when the content is one. */
|
|
34
|
+
role?: "dialog" | "menu" | "listbox" | "group" | undefined;
|
|
35
|
+
/** Place the panel against this element instead of the trigger slot. */
|
|
36
|
+
anchor?: HTMLElement | null | undefined;
|
|
37
|
+
}>(), { placement: "bottom-start", autofocus: true, role: "dialog", anchor: null });
|
|
38
|
+
|
|
39
|
+
const emit = defineEmits<{ (e: "update:open", v: boolean): void }>();
|
|
40
|
+
|
|
41
|
+
const id = `wp-popover-${useId()}`;
|
|
42
|
+
const anchorEl = ref<HTMLElement | null>(null);
|
|
43
|
+
const panelEl = ref<HTMLElement | null>(null);
|
|
44
|
+
const pos = ref<{ x: number; y: number; side: Side; minWidth?: number | undefined }>({ x: -9999, y: -9999, side: "bottom" });
|
|
45
|
+
|
|
46
|
+
const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), '
|
|
47
|
+
+ 'textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
48
|
+
|
|
49
|
+
function trigger(): HTMLElement | null {
|
|
50
|
+
if (props.anchor) return props.anchor;
|
|
51
|
+
const el = anchorEl.value;
|
|
52
|
+
return (el?.firstElementChild as HTMLElement | null) ?? el;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function setOpen(v: boolean): void {
|
|
56
|
+
if (v !== props.open) emit("update:open", v);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const triggerProps = computed(() => ({
|
|
60
|
+
"aria-haspopup": (props.role === "group" ? "true" : props.role) as "true" | "dialog" | "menu" | "listbox",
|
|
61
|
+
"aria-expanded": props.open,
|
|
62
|
+
"aria-controls": props.open ? id : undefined,
|
|
63
|
+
onClick: () => setOpen(!props.open),
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
function place(): void {
|
|
67
|
+
const t = trigger();
|
|
68
|
+
const p = panelEl.value;
|
|
69
|
+
if (!t || !p) return;
|
|
70
|
+
const r = t.getBoundingClientRect();
|
|
71
|
+
const at = placeFloating(r, { width: Math.max(p.offsetWidth, props.matchWidth ? r.width : 0), height: p.offsetHeight }, viewportSize(), {
|
|
72
|
+
placement: props.placement,
|
|
73
|
+
});
|
|
74
|
+
pos.value = { ...at, minWidth: props.matchWidth ? r.width : undefined };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// A Select list, menu or Popover opened from inside this panel is part of it.
|
|
78
|
+
const layer = provideFloatingLayer();
|
|
79
|
+
// And this panel is part of a Popover it was opened from.
|
|
80
|
+
useFloatingChild(() => (props.open ? panelEl.value : null));
|
|
81
|
+
|
|
82
|
+
function onPointerDown(ev: PointerEvent): void {
|
|
83
|
+
const path = ev.composedPath();
|
|
84
|
+
if (layer.owns(path)) return;
|
|
85
|
+
if (panelEl.value && path.includes(panelEl.value)) return;
|
|
86
|
+
if (anchorEl.value && path.includes(anchorEl.value)) return;
|
|
87
|
+
if (props.anchor && path.includes(props.anchor)) return;
|
|
88
|
+
setOpen(false);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function onKeydown(ev: KeyboardEvent): void {
|
|
92
|
+
if (ev.key !== "Escape") return;
|
|
93
|
+
// An open list or menu inside the panel shuts first, on its own handler.
|
|
94
|
+
if (layer.childOpen()) return;
|
|
95
|
+
ev.preventDefault();
|
|
96
|
+
ev.stopPropagation();
|
|
97
|
+
setOpen(false);
|
|
98
|
+
trigger()?.focus();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function listen(on: boolean): void {
|
|
102
|
+
const m = on ? "addEventListener" : "removeEventListener";
|
|
103
|
+
window[m]("pointerdown", onPointerDown as EventListener, true);
|
|
104
|
+
window[m]("keydown", onKeydown as EventListener, true);
|
|
105
|
+
window[m]("resize", place);
|
|
106
|
+
window[m]("scroll", place, true);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
watch(() => props.open, async (v) => {
|
|
110
|
+
if (typeof window === "undefined") return;
|
|
111
|
+
listen(v);
|
|
112
|
+
if (!v) return;
|
|
113
|
+
await nextTick();
|
|
114
|
+
place();
|
|
115
|
+
if (props.autofocus && panelEl.value && !panelEl.value.contains(document.activeElement)) {
|
|
116
|
+
(panelEl.value.querySelector<HTMLElement>(FOCUSABLE) ?? panelEl.value).focus();
|
|
117
|
+
}
|
|
118
|
+
}, { immediate: true });
|
|
119
|
+
|
|
120
|
+
// A shared panel moving to another trigger re-places without closing.
|
|
121
|
+
watch(() => props.anchor, () => { if (props.open) void nextTick(place); });
|
|
122
|
+
|
|
123
|
+
onBeforeUnmount(() => { if (typeof window !== "undefined") listen(false); });
|
|
124
|
+
|
|
125
|
+
defineExpose({ place });
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<template>
|
|
129
|
+
<span v-if="$slots.trigger" ref="anchorEl" class="wp-popover-anchor">
|
|
130
|
+
<slot name="trigger" :props="triggerProps" :open="open" />
|
|
131
|
+
</span>
|
|
132
|
+
<Teleport to="body">
|
|
133
|
+
<div
|
|
134
|
+
v-if="open"
|
|
135
|
+
:id="id"
|
|
136
|
+
ref="panelEl"
|
|
137
|
+
class="wp-popover"
|
|
138
|
+
:data-side="pos.side"
|
|
139
|
+
:role="role"
|
|
140
|
+
:aria-label="label"
|
|
141
|
+
tabindex="-1"
|
|
142
|
+
:style="{ left: `${pos.x}px`, top: `${pos.y}px`, minWidth: pos.minWidth ? `${pos.minWidth}px` : undefined }"
|
|
143
|
+
>
|
|
144
|
+
<slot :close="() => setOpen(false)" />
|
|
145
|
+
</div>
|
|
146
|
+
</Teleport>
|
|
147
|
+
</template>
|
|
148
|
+
|
|
149
|
+
<style>
|
|
150
|
+
@layer wp-extension {
|
|
151
|
+
.wp-popover-anchor { display: inline-flex; }
|
|
152
|
+
.wp-popover {
|
|
153
|
+
position: fixed;
|
|
154
|
+
z-index: 10000;
|
|
155
|
+
max-width: min(420px, calc(100vw - 16px));
|
|
156
|
+
max-height: calc(100vh - 16px);
|
|
157
|
+
overflow: auto;
|
|
158
|
+
padding: var(--wp-space-3);
|
|
159
|
+
background: var(--wp-bg-2);
|
|
160
|
+
color: var(--wp-text);
|
|
161
|
+
border: 1px solid var(--wp-border-strong);
|
|
162
|
+
border-radius: var(--wp-radius);
|
|
163
|
+
box-shadow: var(--wp-shadow-md);
|
|
164
|
+
font-family: var(--wp-font-sans);
|
|
165
|
+
font-size: var(--wp-text-sm);
|
|
166
|
+
outline: none;
|
|
167
|
+
animation: wp-popover-in 120ms ease-out;
|
|
168
|
+
}
|
|
169
|
+
@keyframes wp-popover-in {
|
|
170
|
+
from { opacity: 0; transform: translateY(-2px); }
|
|
171
|
+
}
|
|
172
|
+
.wp-popover[data-side="top"] { animation-name: wp-popover-in-up; }
|
|
173
|
+
@keyframes wp-popover-in-up {
|
|
174
|
+
from { opacity: 0; transform: translateY(2px); }
|
|
175
|
+
}
|
|
176
|
+
@media (prefers-reduced-motion: reduce) {
|
|
177
|
+
.wp-popover { animation: none; }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
</style>
|