@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/DiffView.vue
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Diff view: what changed between two texts, word by word (an option, a
|
|
4
|
-
* template) or line by line (a pack, a log). Removed text is struck through
|
|
5
|
-
* on red, added text is on green, and each change also says "removed" /
|
|
6
|
-
* "added" to screen readers, so colour is never the only sign. Works on the
|
|
7
|
-
* canvas and in the manager.
|
|
8
|
-
*
|
|
9
|
-
* <DiffView before="auburn hair, rim light" after="silver hair, rim light" />
|
|
10
|
-
* <DiffView :before="oldJson" :after="newJson" by="lines" layout="split" />
|
|
11
|
-
*
|
|
12
|
-
* `layout="split"` shows Before and After side by side. Very long texts fall
|
|
13
|
-
* back to "all removed, all added" rather than freeze the page.
|
|
14
|
-
*/
|
|
15
|
-
import { computed } from "vue";
|
|
16
|
-
import { diffText } from "./diff";
|
|
17
|
-
|
|
18
|
-
const props = withDefaults(defineProps<{
|
|
19
|
-
before: string;
|
|
20
|
-
after: string;
|
|
21
|
-
by?: "words" | "lines" | undefined;
|
|
22
|
-
layout?: "inline" | "split" | undefined;
|
|
23
|
-
size?: "sm" | "md" | undefined;
|
|
24
|
-
/** Mono font, for code and JSON. */
|
|
25
|
-
mono?: boolean | undefined;
|
|
26
|
-
}>(), { by: "words", layout: "inline", size: "md" });
|
|
27
|
-
|
|
28
|
-
const parts = computed(() => diffText(props.before, props.after, props.by));
|
|
29
|
-
const counts = computed(() => ({
|
|
30
|
-
add: parts.value.filter((p) => p.kind === "add").length,
|
|
31
|
-
del: parts.value.filter((p) => p.kind === "del").length,
|
|
32
|
-
}));
|
|
33
|
-
</script>
|
|
34
|
-
|
|
35
|
-
<template>
|
|
36
|
-
<div class="wp-diff" :data-size="size" :data-layout="layout" :data-by="by" :data-mono="mono || by === 'lines' || undefined">
|
|
37
|
-
<p v-if="!counts.add && !counts.del" class="wp-diff__same">No changes</p>
|
|
38
|
-
<template v-else-if="layout === 'inline'">
|
|
39
|
-
<div class="wp-diff__text"><template v-for="(p, i) in parts" :key="i"><span v-if="p.kind === 'same'">{{ p.text }}</span><del v-else-if="p.kind === 'del'" class="wp-diff__del"><span class="wp-diff__sr">removed: </span>{{ p.text }}</del><ins v-else class="wp-diff__add"><span class="wp-diff__sr">added: </span>{{ p.text }}</ins></template></div>
|
|
40
|
-
</template>
|
|
41
|
-
<div v-else class="wp-diff__split">
|
|
42
|
-
<section class="wp-diff__side" aria-label="Before">
|
|
43
|
-
<span class="wp-diff__side-label" aria-hidden="true">Before</span>
|
|
44
|
-
<div class="wp-diff__text"><template v-for="(p, i) in parts" :key="i"><span v-if="p.kind === 'same'">{{ p.text }}</span><del v-else-if="p.kind === 'del'" class="wp-diff__del"><span class="wp-diff__sr">removed: </span>{{ p.text }}</del></template></div>
|
|
45
|
-
</section>
|
|
46
|
-
<section class="wp-diff__side" aria-label="After">
|
|
47
|
-
<span class="wp-diff__side-label" aria-hidden="true">After</span>
|
|
48
|
-
<div class="wp-diff__text"><template v-for="(p, i) in parts" :key="i"><span v-if="p.kind === 'same'">{{ p.text }}</span><ins v-else-if="p.kind === 'add'" class="wp-diff__add"><span class="wp-diff__sr">added: </span>{{ p.text }}</ins></template></div>
|
|
49
|
-
</section>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
</template>
|
|
53
|
-
|
|
54
|
-
<style scoped>
|
|
55
|
-
.wp-diff { min-width: 0; color: var(--wp-text); font: 400 var(--wp-text-sm)/1.6 var(--wp-font-sans); }
|
|
56
|
-
.wp-diff[data-size="sm"] { font-size: var(--wp-text-xs); }
|
|
57
|
-
.wp-diff[data-mono] { font-family: var(--wp-font-mono); }
|
|
58
|
-
.wp-diff__text { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
59
|
-
.wp-diff__del, .wp-diff__add { border-radius: min(3px, var(--wp-radius-cap, 999px)); padding: 0 1px; text-decoration-thickness: 1px; }
|
|
60
|
-
.wp-diff__del { background: color-mix(in srgb, var(--wp-danger) 16%, transparent); color: var(--wp-danger-text); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--wp-danger) 70%, transparent); }
|
|
61
|
-
.wp-diff__add { background: color-mix(in srgb, var(--wp-success) 18%, transparent); color: var(--wp-text); text-decoration: none; box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--wp-success) 60%, transparent); }
|
|
62
|
-
.wp-diff[data-by="lines"] .wp-diff__del, .wp-diff[data-by="lines"] .wp-diff__add { display: block; padding: 0 var(--wp-space-2); border-radius: 0; }
|
|
63
|
-
.wp-diff__split { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--wp-space-4); }
|
|
64
|
-
.wp-diff__side { min-width: 0; padding: var(--wp-space-3) var(--wp-space-4); border: 1px solid var(--wp-border); border-radius: var(--wp-radius); background: var(--wp-bg-1); }
|
|
65
|
-
.wp-diff__side-label { display: block; margin-bottom: var(--wp-space-2); color: var(--wp-text-dim); font: 600 var(--wp-text-xs)/1 var(--wp-font-sans); letter-spacing: 0.06em; text-transform: uppercase; }
|
|
66
|
-
.wp-diff__same { margin: 0; color: var(--wp-text-dim); font-style: italic; }
|
|
67
|
-
.wp-diff__sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
68
|
-
</style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Diff view: what changed between two texts, word by word (an option, a
|
|
4
|
+
* template) or line by line (a pack, a log). Removed text is struck through
|
|
5
|
+
* on red, added text is on green, and each change also says "removed" /
|
|
6
|
+
* "added" to screen readers, so colour is never the only sign. Works on the
|
|
7
|
+
* canvas and in the manager.
|
|
8
|
+
*
|
|
9
|
+
* <DiffView before="auburn hair, rim light" after="silver hair, rim light" />
|
|
10
|
+
* <DiffView :before="oldJson" :after="newJson" by="lines" layout="split" />
|
|
11
|
+
*
|
|
12
|
+
* `layout="split"` shows Before and After side by side. Very long texts fall
|
|
13
|
+
* back to "all removed, all added" rather than freeze the page.
|
|
14
|
+
*/
|
|
15
|
+
import { computed } from "vue";
|
|
16
|
+
import { diffText } from "./diff";
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<{
|
|
19
|
+
before: string;
|
|
20
|
+
after: string;
|
|
21
|
+
by?: "words" | "lines" | undefined;
|
|
22
|
+
layout?: "inline" | "split" | undefined;
|
|
23
|
+
size?: "sm" | "md" | undefined;
|
|
24
|
+
/** Mono font, for code and JSON. */
|
|
25
|
+
mono?: boolean | undefined;
|
|
26
|
+
}>(), { by: "words", layout: "inline", size: "md" });
|
|
27
|
+
|
|
28
|
+
const parts = computed(() => diffText(props.before, props.after, props.by));
|
|
29
|
+
const counts = computed(() => ({
|
|
30
|
+
add: parts.value.filter((p) => p.kind === "add").length,
|
|
31
|
+
del: parts.value.filter((p) => p.kind === "del").length,
|
|
32
|
+
}));
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<div class="wp-diff" :data-size="size" :data-layout="layout" :data-by="by" :data-mono="mono || by === 'lines' || undefined">
|
|
37
|
+
<p v-if="!counts.add && !counts.del" class="wp-diff__same">No changes</p>
|
|
38
|
+
<template v-else-if="layout === 'inline'">
|
|
39
|
+
<div class="wp-diff__text"><template v-for="(p, i) in parts" :key="i"><span v-if="p.kind === 'same'">{{ p.text }}</span><del v-else-if="p.kind === 'del'" class="wp-diff__del"><span class="wp-diff__sr">removed: </span>{{ p.text }}</del><ins v-else class="wp-diff__add"><span class="wp-diff__sr">added: </span>{{ p.text }}</ins></template></div>
|
|
40
|
+
</template>
|
|
41
|
+
<div v-else class="wp-diff__split">
|
|
42
|
+
<section class="wp-diff__side" aria-label="Before">
|
|
43
|
+
<span class="wp-diff__side-label" aria-hidden="true">Before</span>
|
|
44
|
+
<div class="wp-diff__text"><template v-for="(p, i) in parts" :key="i"><span v-if="p.kind === 'same'">{{ p.text }}</span><del v-else-if="p.kind === 'del'" class="wp-diff__del"><span class="wp-diff__sr">removed: </span>{{ p.text }}</del></template></div>
|
|
45
|
+
</section>
|
|
46
|
+
<section class="wp-diff__side" aria-label="After">
|
|
47
|
+
<span class="wp-diff__side-label" aria-hidden="true">After</span>
|
|
48
|
+
<div class="wp-diff__text"><template v-for="(p, i) in parts" :key="i"><span v-if="p.kind === 'same'">{{ p.text }}</span><ins v-else-if="p.kind === 'add'" class="wp-diff__add"><span class="wp-diff__sr">added: </span>{{ p.text }}</ins></template></div>
|
|
49
|
+
</section>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<style scoped>
|
|
55
|
+
.wp-diff { min-width: 0; color: var(--wp-text); font: 400 var(--wp-text-sm)/1.6 var(--wp-font-sans); }
|
|
56
|
+
.wp-diff[data-size="sm"] { font-size: var(--wp-text-xs); }
|
|
57
|
+
.wp-diff[data-mono] { font-family: var(--wp-font-mono); }
|
|
58
|
+
.wp-diff__text { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
59
|
+
.wp-diff__del, .wp-diff__add { border-radius: min(3px, var(--wp-radius-cap, 999px)); padding: 0 1px; text-decoration-thickness: 1px; }
|
|
60
|
+
.wp-diff__del { background: color-mix(in srgb, var(--wp-danger) 16%, transparent); color: var(--wp-danger-text); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--wp-danger) 70%, transparent); }
|
|
61
|
+
.wp-diff__add { background: color-mix(in srgb, var(--wp-success) 18%, transparent); color: var(--wp-text); text-decoration: none; box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--wp-success) 60%, transparent); }
|
|
62
|
+
.wp-diff[data-by="lines"] .wp-diff__del, .wp-diff[data-by="lines"] .wp-diff__add { display: block; padding: 0 var(--wp-space-2); border-radius: 0; }
|
|
63
|
+
.wp-diff__split { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--wp-space-4); }
|
|
64
|
+
.wp-diff__side { min-width: 0; padding: var(--wp-space-3) var(--wp-space-4); border: 1px solid var(--wp-border); border-radius: var(--wp-radius); background: var(--wp-bg-1); }
|
|
65
|
+
.wp-diff__side-label { display: block; margin-bottom: var(--wp-space-2); color: var(--wp-text-dim); font: 600 var(--wp-text-xs)/1 var(--wp-font-sans); letter-spacing: 0.06em; text-transform: uppercase; }
|
|
66
|
+
.wp-diff__same { margin: 0; color: var(--wp-text-dim); font-style: italic; }
|
|
67
|
+
.wp-diff__sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
68
|
+
</style>
|
package/src/Disclosure.vue
CHANGED
|
@@ -1,166 +1,166 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Disclosure: a "show more" section that folds open under its heading. The
|
|
4
|
-
* heading is a button with `aria-expanded` + `aria-controls` (APG
|
|
5
|
-
* disclosure), the chevron turns when open, and the body grows to its height
|
|
6
|
-
* (no motion with `prefers-reduced-motion`). Works on the canvas and in the
|
|
7
|
-
* manager.
|
|
8
|
-
*
|
|
9
|
-
* <Disclosure v-model:open="open" label="Advanced">…</Disclosure>
|
|
10
|
-
* <Disclosure label="Rules" :count="3"><template #summary>…</template>…</Disclosure>
|
|
11
|
-
*
|
|
12
|
-
* Leave out `v-model:open` and it keeps its own state (`default-open` sets
|
|
13
|
-
* where it starts). `#actions` sits at the right end of the heading, outside
|
|
14
|
-
* the button, for controls that should not fold the section; `#lead` sits
|
|
15
|
-
* before the button for the same (a "select all" checkbox lined up with
|
|
16
|
-
* the rows' own). `lazy` builds
|
|
17
|
-
* the body only while it is open. `flush` makes the heading a full-width
|
|
18
|
-
* band with a rule under it and drops the body's indent, for a section of a
|
|
19
|
-
* table or list that fills its card (a group of options, a rule).
|
|
20
|
-
*/
|
|
21
|
-
import { computed, ref, useId, useSlots } from "vue";
|
|
22
|
-
|
|
23
|
-
const props = withDefaults(defineProps<{
|
|
24
|
-
open?: boolean | undefined;
|
|
25
|
-
/** Where it starts when nobody binds `open`. */
|
|
26
|
-
defaultOpen?: boolean | undefined;
|
|
27
|
-
label?: string | undefined;
|
|
28
|
-
/** A small count after the label. */
|
|
29
|
-
count?: number | string | undefined;
|
|
30
|
-
size?: "sm" | "md" | undefined;
|
|
31
|
-
disabled?: boolean | undefined;
|
|
32
|
-
/** Draw a border and a padded body, for a section that stands alone. */
|
|
33
|
-
boxed?: boolean | undefined;
|
|
34
|
-
/** Full-width heading band, body without indent: a section of a table or list. */
|
|
35
|
-
flush?: boolean | undefined;
|
|
36
|
-
/** Build the body only while open (a long list, a costly panel). */
|
|
37
|
-
lazy?: boolean | undefined;
|
|
38
|
-
/** Extra attributes on the heading button, e.g. `data-test`. */
|
|
39
|
-
triggerAttrs?: Record<string, string> | undefined;
|
|
40
|
-
}>(), { open: undefined, size: "md", triggerAttrs: undefined });
|
|
41
|
-
|
|
42
|
-
const emit = defineEmits<{ (e: "update:open", v: boolean): void }>();
|
|
43
|
-
const slots = useSlots();
|
|
44
|
-
|
|
45
|
-
const own = ref(props.defaultOpen);
|
|
46
|
-
const isOpen = computed(() => props.open ?? own.value);
|
|
47
|
-
const bodyId = `wp-disclosure-${useId()}`;
|
|
48
|
-
|
|
49
|
-
function toggle(): void {
|
|
50
|
-
if (props.disabled) return;
|
|
51
|
-
const next = !isOpen.value;
|
|
52
|
-
own.value = next;
|
|
53
|
-
emit("update:open", next);
|
|
54
|
-
}
|
|
55
|
-
</script>
|
|
56
|
-
|
|
57
|
-
<template>
|
|
58
|
-
<div class="wp-disclosure" :data-size="size" :data-open="isOpen" :data-boxed="boxed || undefined" :data-flush="flush || undefined">
|
|
59
|
-
<div class="wp-disclosure__head">
|
|
60
|
-
<div v-if="slots.lead" class="wp-disclosure__lead"><slot name="lead" /></div>
|
|
61
|
-
<button
|
|
62
|
-
v-bind="triggerAttrs ?? {}"
|
|
63
|
-
type="button"
|
|
64
|
-
class="wp-disclosure__trigger"
|
|
65
|
-
:aria-expanded="isOpen"
|
|
66
|
-
:aria-controls="bodyId"
|
|
67
|
-
:disabled="disabled"
|
|
68
|
-
@click="toggle"
|
|
69
|
-
>
|
|
70
|
-
<i class="pi pi-chevron-right wp-disclosure__chev" aria-hidden="true" />
|
|
71
|
-
<slot name="summary">
|
|
72
|
-
<span class="wp-disclosure__label">{{ label }}</span>
|
|
73
|
-
</slot>
|
|
74
|
-
<span v-if="count !== undefined" class="wp-disclosure__count">{{ count }}</span>
|
|
75
|
-
</button>
|
|
76
|
-
<div v-if="slots.actions" class="wp-disclosure__actions"><slot name="actions" /></div>
|
|
77
|
-
</div>
|
|
78
|
-
<div :id="bodyId" class="wp-disclosure__body" :inert="!isOpen || undefined">
|
|
79
|
-
<div class="wp-disclosure__inner">
|
|
80
|
-
<div v-if="isOpen || !lazy" class="wp-disclosure__content"><slot /></div>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
</template>
|
|
85
|
-
|
|
86
|
-
<style scoped>
|
|
87
|
-
.wp-disclosure { min-width: 0; }
|
|
88
|
-
.wp-disclosure[data-boxed] {
|
|
89
|
-
border: 1px solid var(--wp-border);
|
|
90
|
-
border-radius: var(--wp-radius-md);
|
|
91
|
-
background: var(--wp-bg-1);
|
|
92
|
-
}
|
|
93
|
-
.wp-disclosure__head { display: flex; align-items: center; gap: var(--wp-space-3); min-width: 0; }
|
|
94
|
-
.wp-disclosure[data-boxed] .wp-disclosure__head { padding: 0 var(--wp-space-3); }
|
|
95
|
-
.wp-disclosure__trigger {
|
|
96
|
-
display: inline-flex;
|
|
97
|
-
align-items: center;
|
|
98
|
-
gap: var(--wp-space-3);
|
|
99
|
-
flex: 1;
|
|
100
|
-
min-width: 0;
|
|
101
|
-
min-height: var(--wp-control-h-md);
|
|
102
|
-
margin: 0;
|
|
103
|
-
padding: 0 var(--wp-space-2);
|
|
104
|
-
border: 0;
|
|
105
|
-
border-radius: var(--wp-radius-sm);
|
|
106
|
-
background: transparent;
|
|
107
|
-
color: var(--wp-text);
|
|
108
|
-
font: 600 var(--wp-text-sm)/1.3 var(--wp-font-sans);
|
|
109
|
-
text-align: left;
|
|
110
|
-
cursor: pointer;
|
|
111
|
-
}
|
|
112
|
-
.wp-disclosure[data-size="sm"] .wp-disclosure__trigger {
|
|
113
|
-
min-height: var(--wp-control-h-sm);
|
|
114
|
-
font-size: var(--wp-text-xs);
|
|
115
|
-
gap: var(--wp-space-2);
|
|
116
|
-
}
|
|
117
|
-
.wp-disclosure__trigger:hover:not(:disabled) { background: var(--wp-row-hover); }
|
|
118
|
-
.wp-disclosure__trigger:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--wp-border-focus); }
|
|
119
|
-
.wp-disclosure__trigger:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
120
|
-
.wp-disclosure__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
121
|
-
.wp-disclosure__chev {
|
|
122
|
-
flex: none;
|
|
123
|
-
font-size: 0.75em;
|
|
124
|
-
color: var(--wp-text-muted);
|
|
125
|
-
transition: transform var(--wp-motion-swap) var(--wp-motion-curve-collapse);
|
|
126
|
-
}
|
|
127
|
-
.wp-disclosure[data-open="true"] .wp-disclosure__chev { transform: rotate(90deg); }
|
|
128
|
-
.wp-disclosure__count {
|
|
129
|
-
padding: 1px 6px;
|
|
130
|
-
border-radius: var(--wp-radius-cap, 999px);
|
|
131
|
-
background: var(--wp-bg-3);
|
|
132
|
-
color: var(--wp-text-muted);
|
|
133
|
-
font-size: var(--wp-text-xs);
|
|
134
|
-
font-weight: 500;
|
|
135
|
-
font-variant-numeric: tabular-nums;
|
|
136
|
-
}
|
|
137
|
-
.wp-disclosure__lead { display: inline-flex; align-items: center; flex: none; }
|
|
138
|
-
.wp-disclosure__actions { display: inline-flex; align-items: center; gap: var(--wp-space-2); flex: none; }
|
|
139
|
-
/* Grid rows animate from 0fr to 1fr, so the body grows to its own height
|
|
140
|
-
without measuring it. */
|
|
141
|
-
.wp-disclosure__body {
|
|
142
|
-
display: grid;
|
|
143
|
-
grid-template-rows: 0fr;
|
|
144
|
-
transition: grid-template-rows var(--wp-motion-swap) var(--wp-motion-curve-collapse);
|
|
145
|
-
}
|
|
146
|
-
.wp-disclosure[data-open="true"] > .wp-disclosure__body { grid-template-rows: 1fr; }
|
|
147
|
-
.wp-disclosure__inner { min-height: 0; overflow: hidden; }
|
|
148
|
-
.wp-disclosure__content { padding: var(--wp-space-3) 0 var(--wp-space-2) calc(var(--wp-space-2) + 1.1em + var(--wp-space-3)); }
|
|
149
|
-
.wp-disclosure[data-size="sm"] .wp-disclosure__content { padding: var(--wp-space-2) 0 var(--wp-space-2) calc(var(--wp-space-2) + 1em); }
|
|
150
|
-
.wp-disclosure[data-boxed] .wp-disclosure__content { padding: var(--wp-space-2) var(--wp-space-5) var(--wp-space-5); }
|
|
151
|
-
.wp-disclosure[data-flush] > .wp-disclosure__head {
|
|
152
|
-
border-bottom: 1px solid var(--wp-border);
|
|
153
|
-
background: var(--wp-bg-2);
|
|
154
|
-
}
|
|
155
|
-
.wp-disclosure[data-flush] .wp-disclosure__trigger {
|
|
156
|
-
padding: 0 var(--wp-space-5);
|
|
157
|
-
border-radius: 0;
|
|
158
|
-
font-size: var(--wp-text-xs);
|
|
159
|
-
}
|
|
160
|
-
.wp-disclosure[data-flush] .wp-disclosure__trigger:focus-visible { box-shadow: inset 0 0 0 2px var(--wp-border-focus); }
|
|
161
|
-
.wp-disclosure[data-flush] .wp-disclosure__content,
|
|
162
|
-
.wp-disclosure[data-flush][data-size="sm"] .wp-disclosure__content { padding: 0; }
|
|
163
|
-
@media (prefers-reduced-motion: reduce) {
|
|
164
|
-
.wp-disclosure__chev, .wp-disclosure__body { transition: none; }
|
|
165
|
-
}
|
|
166
|
-
</style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Disclosure: a "show more" section that folds open under its heading. The
|
|
4
|
+
* heading is a button with `aria-expanded` + `aria-controls` (APG
|
|
5
|
+
* disclosure), the chevron turns when open, and the body grows to its height
|
|
6
|
+
* (no motion with `prefers-reduced-motion`). Works on the canvas and in the
|
|
7
|
+
* manager.
|
|
8
|
+
*
|
|
9
|
+
* <Disclosure v-model:open="open" label="Advanced">…</Disclosure>
|
|
10
|
+
* <Disclosure label="Rules" :count="3"><template #summary>…</template>…</Disclosure>
|
|
11
|
+
*
|
|
12
|
+
* Leave out `v-model:open` and it keeps its own state (`default-open` sets
|
|
13
|
+
* where it starts). `#actions` sits at the right end of the heading, outside
|
|
14
|
+
* the button, for controls that should not fold the section; `#lead` sits
|
|
15
|
+
* before the button for the same (a "select all" checkbox lined up with
|
|
16
|
+
* the rows' own). `lazy` builds
|
|
17
|
+
* the body only while it is open. `flush` makes the heading a full-width
|
|
18
|
+
* band with a rule under it and drops the body's indent, for a section of a
|
|
19
|
+
* table or list that fills its card (a group of options, a rule).
|
|
20
|
+
*/
|
|
21
|
+
import { computed, ref, useId, useSlots } from "vue";
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<{
|
|
24
|
+
open?: boolean | undefined;
|
|
25
|
+
/** Where it starts when nobody binds `open`. */
|
|
26
|
+
defaultOpen?: boolean | undefined;
|
|
27
|
+
label?: string | undefined;
|
|
28
|
+
/** A small count after the label. */
|
|
29
|
+
count?: number | string | undefined;
|
|
30
|
+
size?: "sm" | "md" | undefined;
|
|
31
|
+
disabled?: boolean | undefined;
|
|
32
|
+
/** Draw a border and a padded body, for a section that stands alone. */
|
|
33
|
+
boxed?: boolean | undefined;
|
|
34
|
+
/** Full-width heading band, body without indent: a section of a table or list. */
|
|
35
|
+
flush?: boolean | undefined;
|
|
36
|
+
/** Build the body only while open (a long list, a costly panel). */
|
|
37
|
+
lazy?: boolean | undefined;
|
|
38
|
+
/** Extra attributes on the heading button, e.g. `data-test`. */
|
|
39
|
+
triggerAttrs?: Record<string, string> | undefined;
|
|
40
|
+
}>(), { open: undefined, size: "md", triggerAttrs: undefined });
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits<{ (e: "update:open", v: boolean): void }>();
|
|
43
|
+
const slots = useSlots();
|
|
44
|
+
|
|
45
|
+
const own = ref(props.defaultOpen);
|
|
46
|
+
const isOpen = computed(() => props.open ?? own.value);
|
|
47
|
+
const bodyId = `wp-disclosure-${useId()}`;
|
|
48
|
+
|
|
49
|
+
function toggle(): void {
|
|
50
|
+
if (props.disabled) return;
|
|
51
|
+
const next = !isOpen.value;
|
|
52
|
+
own.value = next;
|
|
53
|
+
emit("update:open", next);
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<div class="wp-disclosure" :data-size="size" :data-open="isOpen" :data-boxed="boxed || undefined" :data-flush="flush || undefined">
|
|
59
|
+
<div class="wp-disclosure__head">
|
|
60
|
+
<div v-if="slots.lead" class="wp-disclosure__lead"><slot name="lead" /></div>
|
|
61
|
+
<button
|
|
62
|
+
v-bind="triggerAttrs ?? {}"
|
|
63
|
+
type="button"
|
|
64
|
+
class="wp-disclosure__trigger"
|
|
65
|
+
:aria-expanded="isOpen"
|
|
66
|
+
:aria-controls="bodyId"
|
|
67
|
+
:disabled="disabled"
|
|
68
|
+
@click="toggle"
|
|
69
|
+
>
|
|
70
|
+
<i class="pi pi-chevron-right wp-disclosure__chev" aria-hidden="true" />
|
|
71
|
+
<slot name="summary">
|
|
72
|
+
<span class="wp-disclosure__label">{{ label }}</span>
|
|
73
|
+
</slot>
|
|
74
|
+
<span v-if="count !== undefined" class="wp-disclosure__count">{{ count }}</span>
|
|
75
|
+
</button>
|
|
76
|
+
<div v-if="slots.actions" class="wp-disclosure__actions"><slot name="actions" /></div>
|
|
77
|
+
</div>
|
|
78
|
+
<div :id="bodyId" class="wp-disclosure__body" :inert="!isOpen || undefined">
|
|
79
|
+
<div class="wp-disclosure__inner">
|
|
80
|
+
<div v-if="isOpen || !lazy" class="wp-disclosure__content"><slot /></div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</template>
|
|
85
|
+
|
|
86
|
+
<style scoped>
|
|
87
|
+
.wp-disclosure { min-width: 0; }
|
|
88
|
+
.wp-disclosure[data-boxed] {
|
|
89
|
+
border: 1px solid var(--wp-border);
|
|
90
|
+
border-radius: var(--wp-radius-md);
|
|
91
|
+
background: var(--wp-bg-1);
|
|
92
|
+
}
|
|
93
|
+
.wp-disclosure__head { display: flex; align-items: center; gap: var(--wp-space-3); min-width: 0; }
|
|
94
|
+
.wp-disclosure[data-boxed] .wp-disclosure__head { padding: 0 var(--wp-space-3); }
|
|
95
|
+
.wp-disclosure__trigger {
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
gap: var(--wp-space-3);
|
|
99
|
+
flex: 1;
|
|
100
|
+
min-width: 0;
|
|
101
|
+
min-height: var(--wp-control-h-md);
|
|
102
|
+
margin: 0;
|
|
103
|
+
padding: 0 var(--wp-space-2);
|
|
104
|
+
border: 0;
|
|
105
|
+
border-radius: var(--wp-radius-sm);
|
|
106
|
+
background: transparent;
|
|
107
|
+
color: var(--wp-text);
|
|
108
|
+
font: 600 var(--wp-text-sm)/1.3 var(--wp-font-sans);
|
|
109
|
+
text-align: left;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
}
|
|
112
|
+
.wp-disclosure[data-size="sm"] .wp-disclosure__trigger {
|
|
113
|
+
min-height: var(--wp-control-h-sm);
|
|
114
|
+
font-size: var(--wp-text-xs);
|
|
115
|
+
gap: var(--wp-space-2);
|
|
116
|
+
}
|
|
117
|
+
.wp-disclosure__trigger:hover:not(:disabled) { background: var(--wp-row-hover); }
|
|
118
|
+
.wp-disclosure__trigger:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--wp-border-focus); }
|
|
119
|
+
.wp-disclosure__trigger:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
120
|
+
.wp-disclosure__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
121
|
+
.wp-disclosure__chev {
|
|
122
|
+
flex: none;
|
|
123
|
+
font-size: 0.75em;
|
|
124
|
+
color: var(--wp-text-muted);
|
|
125
|
+
transition: transform var(--wp-motion-swap) var(--wp-motion-curve-collapse);
|
|
126
|
+
}
|
|
127
|
+
.wp-disclosure[data-open="true"] .wp-disclosure__chev { transform: rotate(90deg); }
|
|
128
|
+
.wp-disclosure__count {
|
|
129
|
+
padding: 1px 6px;
|
|
130
|
+
border-radius: var(--wp-radius-cap, 999px);
|
|
131
|
+
background: var(--wp-bg-3);
|
|
132
|
+
color: var(--wp-text-muted);
|
|
133
|
+
font-size: var(--wp-text-xs);
|
|
134
|
+
font-weight: 500;
|
|
135
|
+
font-variant-numeric: tabular-nums;
|
|
136
|
+
}
|
|
137
|
+
.wp-disclosure__lead { display: inline-flex; align-items: center; flex: none; }
|
|
138
|
+
.wp-disclosure__actions { display: inline-flex; align-items: center; gap: var(--wp-space-2); flex: none; }
|
|
139
|
+
/* Grid rows animate from 0fr to 1fr, so the body grows to its own height
|
|
140
|
+
without measuring it. */
|
|
141
|
+
.wp-disclosure__body {
|
|
142
|
+
display: grid;
|
|
143
|
+
grid-template-rows: 0fr;
|
|
144
|
+
transition: grid-template-rows var(--wp-motion-swap) var(--wp-motion-curve-collapse);
|
|
145
|
+
}
|
|
146
|
+
.wp-disclosure[data-open="true"] > .wp-disclosure__body { grid-template-rows: 1fr; }
|
|
147
|
+
.wp-disclosure__inner { min-height: 0; overflow: hidden; }
|
|
148
|
+
.wp-disclosure__content { padding: var(--wp-space-3) 0 var(--wp-space-2) calc(var(--wp-space-2) + 1.1em + var(--wp-space-3)); }
|
|
149
|
+
.wp-disclosure[data-size="sm"] .wp-disclosure__content { padding: var(--wp-space-2) 0 var(--wp-space-2) calc(var(--wp-space-2) + 1em); }
|
|
150
|
+
.wp-disclosure[data-boxed] .wp-disclosure__content { padding: var(--wp-space-2) var(--wp-space-5) var(--wp-space-5); }
|
|
151
|
+
.wp-disclosure[data-flush] > .wp-disclosure__head {
|
|
152
|
+
border-bottom: 1px solid var(--wp-border);
|
|
153
|
+
background: var(--wp-bg-2);
|
|
154
|
+
}
|
|
155
|
+
.wp-disclosure[data-flush] .wp-disclosure__trigger {
|
|
156
|
+
padding: 0 var(--wp-space-5);
|
|
157
|
+
border-radius: 0;
|
|
158
|
+
font-size: var(--wp-text-xs);
|
|
159
|
+
}
|
|
160
|
+
.wp-disclosure[data-flush] .wp-disclosure__trigger:focus-visible { box-shadow: inset 0 0 0 2px var(--wp-border-focus); }
|
|
161
|
+
.wp-disclosure[data-flush] .wp-disclosure__content,
|
|
162
|
+
.wp-disclosure[data-flush][data-size="sm"] .wp-disclosure__content { padding: 0; }
|
|
163
|
+
@media (prefers-reduced-motion: reduce) {
|
|
164
|
+
.wp-disclosure__chev, .wp-disclosure__body { transition: none; }
|
|
165
|
+
}
|
|
166
|
+
</style>
|
package/src/DragHandle.vue
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* The six-dot grip on a row you drag to reorder (a module row, a bundle
|
|
4
|
-
* child, an injector binding). It only shows where to grab: the row owns the
|
|
5
|
-
* drag (HTML drag-and-drop or pointer events), so the grip is hidden from
|
|
6
|
-
* screen readers and the row gives keyboard users their own way to move it.
|
|
7
|
-
* For a list that the kit should reorder itself, with keyboard moves, use
|
|
8
|
-
* SortableList.
|
|
9
|
-
*
|
|
10
|
-
* <DragHandle /> the canvas row size
|
|
11
|
-
* <DragHandle size="md" /> a manager row
|
|
12
|
-
*
|
|
13
|
-
* Reads only the shared tokens, so it works on the canvas and on a page.
|
|
14
|
-
*/
|
|
15
|
-
withDefaults(defineProps<{
|
|
16
|
-
size?: "sm" | "md" | undefined;
|
|
17
|
-
/** Tooltip on hover. */
|
|
18
|
-
title?: string | undefined;
|
|
19
|
-
}>(), { size: "sm", title: "Drag to reorder" });
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<template>
|
|
23
|
-
<span class="wp-drag-grip" :data-size="size" :title="title" aria-hidden="true">
|
|
24
|
-
<svg viewBox="0 0 6 12" fill="currentColor" focusable="false">
|
|
25
|
-
<circle cx="1.5" cy="2" r="1" />
|
|
26
|
-
<circle cx="4.5" cy="2" r="1" />
|
|
27
|
-
<circle cx="1.5" cy="6" r="1" />
|
|
28
|
-
<circle cx="4.5" cy="6" r="1" />
|
|
29
|
-
<circle cx="1.5" cy="10" r="1" />
|
|
30
|
-
<circle cx="4.5" cy="10" r="1" />
|
|
31
|
-
</svg>
|
|
32
|
-
</span>
|
|
33
|
-
</template>
|
|
34
|
-
|
|
35
|
-
<style scoped>
|
|
36
|
-
.wp-drag-grip {
|
|
37
|
-
display: inline-grid;
|
|
38
|
-
place-items: center;
|
|
39
|
-
flex: none;
|
|
40
|
-
width: 12px;
|
|
41
|
-
height: 20px;
|
|
42
|
-
color: var(--wp-text-dim);
|
|
43
|
-
cursor: grab;
|
|
44
|
-
}
|
|
45
|
-
.wp-drag-grip[data-size="md"] { width: 16px; height: 24px; }
|
|
46
|
-
.wp-drag-grip svg { width: 6px; height: 12px; }
|
|
47
|
-
.wp-drag-grip[data-size="md"] svg { width: 8px; height: 16px; }
|
|
48
|
-
.wp-drag-grip:hover { color: var(--wp-text-muted); }
|
|
49
|
-
</style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The six-dot grip on a row you drag to reorder (a module row, a bundle
|
|
4
|
+
* child, an injector binding). It only shows where to grab: the row owns the
|
|
5
|
+
* drag (HTML drag-and-drop or pointer events), so the grip is hidden from
|
|
6
|
+
* screen readers and the row gives keyboard users their own way to move it.
|
|
7
|
+
* For a list that the kit should reorder itself, with keyboard moves, use
|
|
8
|
+
* SortableList.
|
|
9
|
+
*
|
|
10
|
+
* <DragHandle /> the canvas row size
|
|
11
|
+
* <DragHandle size="md" /> a manager row
|
|
12
|
+
*
|
|
13
|
+
* Reads only the shared tokens, so it works on the canvas and on a page.
|
|
14
|
+
*/
|
|
15
|
+
withDefaults(defineProps<{
|
|
16
|
+
size?: "sm" | "md" | undefined;
|
|
17
|
+
/** Tooltip on hover. */
|
|
18
|
+
title?: string | undefined;
|
|
19
|
+
}>(), { size: "sm", title: "Drag to reorder" });
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<span class="wp-drag-grip" :data-size="size" :title="title" aria-hidden="true">
|
|
24
|
+
<svg viewBox="0 0 6 12" fill="currentColor" focusable="false">
|
|
25
|
+
<circle cx="1.5" cy="2" r="1" />
|
|
26
|
+
<circle cx="4.5" cy="2" r="1" />
|
|
27
|
+
<circle cx="1.5" cy="6" r="1" />
|
|
28
|
+
<circle cx="4.5" cy="6" r="1" />
|
|
29
|
+
<circle cx="1.5" cy="10" r="1" />
|
|
30
|
+
<circle cx="4.5" cy="10" r="1" />
|
|
31
|
+
</svg>
|
|
32
|
+
</span>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<style scoped>
|
|
36
|
+
.wp-drag-grip {
|
|
37
|
+
display: inline-grid;
|
|
38
|
+
place-items: center;
|
|
39
|
+
flex: none;
|
|
40
|
+
width: 12px;
|
|
41
|
+
height: 20px;
|
|
42
|
+
color: var(--wp-text-dim);
|
|
43
|
+
cursor: grab;
|
|
44
|
+
}
|
|
45
|
+
.wp-drag-grip[data-size="md"] { width: 16px; height: 24px; }
|
|
46
|
+
.wp-drag-grip svg { width: 6px; height: 12px; }
|
|
47
|
+
.wp-drag-grip[data-size="md"] svg { width: 8px; height: 16px; }
|
|
48
|
+
.wp-drag-grip:hover { color: var(--wp-text-muted); }
|
|
49
|
+
</style>
|