@dumiflex/wp-kit 0.3.0 → 0.4.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 +77 -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 +119 -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 +292 -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 +125 -125
- package/src/InlineEdit.vue +183 -183
- package/src/Input.vue +99 -99
- package/src/Kbd.vue +56 -56
- package/src/Lightbox.vue +173 -173
- package/src/ListFilter.vue +85 -85
- package/src/ListRow.vue +204 -204
- package/src/LoadingMark.vue +175 -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 +82 -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/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/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/LoadingMark.vue
CHANGED
|
@@ -1,161 +1,175 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** Gradient ids must be unique on the page. `useId` restarts per Vue app and
|
|
3
|
-
* the extension mounts many small apps, so count across all of them. */
|
|
4
|
-
let nextId = 0;
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<script setup lang="ts">
|
|
8
|
-
/**
|
|
9
|
-
* The loading mark. Two drawings, one per kind of place:
|
|
10
|
-
*
|
|
11
|
-
* - `logo` (pages and panels): the full logo. Its two rings slide apart and
|
|
12
|
-
* back while the big star dips and turns and the small stars twinkle. A
|
|
13
|
-
* `block` mark, or one 24 px or bigger, gets it by default.
|
|
14
|
-
* - `sparkle` (tight inline spots: a text row, a button, a list item): the
|
|
15
|
-
* logo's three stars without the rings, the big one dipping while the small
|
|
16
|
-
* ones swap places, like the AI working line. Small marks get it by
|
|
17
|
-
* default, so an inline "Loading bundle…" is not a big colourful logo.
|
|
18
|
-
*
|
|
19
|
-
* Alone it is an icon (`size`, 14 inline and 40 as a block). With slot text it
|
|
20
|
-
* becomes a row ("[mark] Loading bundle…"); with `block` it is centred in its
|
|
21
|
-
* box, for a panel or page that is loading. `mono` draws it in the text
|
|
22
|
-
* colour, for a host with its own colours (a busy button).
|
|
23
|
-
*/
|
|
24
|
-
import { computed, useSlots } from "vue";
|
|
25
|
-
import { BRAND_COLOURS, RING_A, RING_B, STAR_A, STAR_B, STAR_BIG, type LogoBrand } from "./logo-paths";
|
|
26
|
-
|
|
27
|
-
const props = withDefaults(defineProps<{
|
|
28
|
-
/** Width and height in px. Default 14, or 40 with `block`. */
|
|
29
|
-
size?: number | undefined;
|
|
30
|
-
/** Accessible name when there is no slot text. */
|
|
31
|
-
label?: string | undefined;
|
|
32
|
-
/** Centre it (and its text, below) in the space it is given. */
|
|
33
|
-
block?: boolean | undefined;
|
|
34
|
-
/** Which drawing; default `logo` for a block or a 24 px+ mark, else `sparkle`. */
|
|
35
|
-
variant?: "logo" | "sparkle" | undefined;
|
|
36
|
-
/** Draw in the current text colour instead of the logo colours. */
|
|
37
|
-
mono?: boolean | undefined;
|
|
38
|
-
/** Hidden from screen readers, for a host that says it is busy itself
|
|
39
|
-
* (a button with `aria-busy`). */
|
|
40
|
-
decorative?: boolean | undefined;
|
|
41
|
-
/** Whose colours: the extension's (default) or the community site's. */
|
|
42
|
-
brand?: LogoBrand | undefined;
|
|
43
|
-
}>(), { size: undefined, label: "Loading", block: false, variant: undefined, mono: false, decorative: false, brand: "extension" });
|
|
44
|
-
|
|
45
|
-
const slots = useSlots();
|
|
46
|
-
const id = `wp-lm-${nextId++}`;
|
|
47
|
-
const px = computed(() => props.size ?? (props.block ? 40 : 14));
|
|
48
|
-
const drawing = computed(() => props.variant ?? (props.block || px.value >= 24 ? "logo" : "sparkle"));
|
|
49
|
-
/** The logo's own colours (brand, not theme tokens: the mark looks the same
|
|
50
|
-
* in light and dark). */
|
|
51
|
-
const VIOLET = computed(() => BRAND_COLOURS[props.brand][0]);
|
|
52
|
-
const INDIGO = computed(() => BRAND_COLOURS[props.brand][1]);
|
|
53
|
-
const TEAL = computed(() => BRAND_COLOURS[props.brand][2]);
|
|
54
|
-
const fill = (colour: string): string => (props.mono ? "currentColor" : colour);
|
|
55
|
-
</script>
|
|
56
|
-
|
|
57
|
-
<template>
|
|
58
|
-
<span
|
|
59
|
-
class="wp-lm"
|
|
60
|
-
:class="{ 'wp-lm--block': block }"
|
|
61
|
-
:role="decorative ? undefined : 'status'"
|
|
62
|
-
:aria-label="decorative || slots.default ? undefined : label"
|
|
63
|
-
:aria-hidden="decorative || undefined"
|
|
64
|
-
data-test="loading-mark"
|
|
65
|
-
>
|
|
66
|
-
<svg
|
|
67
|
-
class="wp-lm__svg"
|
|
68
|
-
:data-variant="drawing"
|
|
69
|
-
viewBox="0 0 1024 1024"
|
|
70
|
-
:width="px"
|
|
71
|
-
:height="px"
|
|
72
|
-
aria-hidden="true"
|
|
73
|
-
>
|
|
74
|
-
<template v-if="drawing === 'logo'">
|
|
75
|
-
<defs v-if="!mono">
|
|
76
|
-
<linearGradient :id="`${id}a`" x1="312.56" y1="897.19" x2="312.56" y2="2" gradientUnits="userSpaceOnUse">
|
|
77
|
-
<stop offset="0" :stop-color="VIOLET" />
|
|
78
|
-
<stop offset="1" :stop-color="INDIGO" />
|
|
79
|
-
</linearGradient>
|
|
80
|
-
<linearGradient :id="`${id}b`" x1="711.44" y1="126.81" x2="711.44" y2="1022" gradientUnits="userSpaceOnUse">
|
|
81
|
-
<stop offset="0" :stop-color="INDIGO" />
|
|
82
|
-
<stop offset="1" :stop-color="TEAL" />
|
|
83
|
-
</linearGradient>
|
|
84
|
-
</defs>
|
|
85
|
-
<path class="wp-lm__ring wp-lm__ring--a" :fill="fill(`url(#${id}a)`)" :d="RING_A" />
|
|
86
|
-
<path class="wp-lm__ring wp-lm__ring--b" :fill="fill(`url(#${id}b)`)" :d="RING_B" />
|
|
87
|
-
<path class="wp-lm__star wp-lm__star--big" :fill="fill(VIOLET)" :d="STAR_BIG" />
|
|
88
|
-
<path class="wp-lm__star wp-lm__star--tw" :fill="fill(INDIGO)" :d="STAR_A" />
|
|
89
|
-
<path class="wp-lm__star wp-lm__star--tw" :fill="fill(TEAL)" :d="STAR_B" />
|
|
90
|
-
</template>
|
|
91
|
-
<g v-else transform="translate(512 512) scale(1.45) translate(-512 -512)">
|
|
92
|
-
<path class="wp-lm__star wp-lm__star--big" :fill="fill(VIOLET)" :d="STAR_BIG" />
|
|
93
|
-
<g class="wp-lm__swap">
|
|
94
|
-
<path class="wp-lm__star" :fill="fill(INDIGO)" :d="STAR_A" />
|
|
95
|
-
<path class="wp-lm__star" :fill="fill(TEAL)" :d="STAR_B" />
|
|
96
|
-
</g>
|
|
97
|
-
</g>
|
|
98
|
-
</svg>
|
|
99
|
-
<span v-if="slots.default" class="wp-lm__text"><slot /></span>
|
|
100
|
-
</span>
|
|
101
|
-
</template>
|
|
102
|
-
|
|
103
|
-
<style scoped>
|
|
104
|
-
.wp-lm {
|
|
105
|
-
--wp-lm-t: 2.4s;
|
|
106
|
-
display: inline-flex;
|
|
107
|
-
align-items: center;
|
|
108
|
-
gap: 8px;
|
|
109
|
-
vertical-align: middle;
|
|
110
|
-
}
|
|
111
|
-
.wp-lm--block {
|
|
112
|
-
display: flex;
|
|
113
|
-
flex-direction: column;
|
|
114
|
-
justify-content: center;
|
|
115
|
-
gap: 12px;
|
|
116
|
-
width: 100%;
|
|
117
|
-
padding: 48px 16px;
|
|
118
|
-
color: var(--wp-text-muted);
|
|
119
|
-
text-align: center;
|
|
120
|
-
}
|
|
121
|
-
.wp-lm__svg { flex-shrink: 0; overflow: visible; }
|
|
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
|
-
@keyframes wp-lm-
|
|
152
|
-
0%, 100% {
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** Gradient ids must be unique on the page. `useId` restarts per Vue app and
|
|
3
|
+
* the extension mounts many small apps, so count across all of them. */
|
|
4
|
+
let nextId = 0;
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
/**
|
|
9
|
+
* The loading mark. Two drawings, one per kind of place:
|
|
10
|
+
*
|
|
11
|
+
* - `logo` (pages and panels): the full logo. Its two rings slide apart and
|
|
12
|
+
* back while the big star dips and turns and the small stars twinkle. A
|
|
13
|
+
* `block` mark, or one 24 px or bigger, gets it by default.
|
|
14
|
+
* - `sparkle` (tight inline spots: a text row, a button, a list item): the
|
|
15
|
+
* logo's three stars without the rings, the big one dipping while the small
|
|
16
|
+
* ones swap places, like the AI working line. Small marks get it by
|
|
17
|
+
* default, so an inline "Loading bundle…" is not a big colourful logo.
|
|
18
|
+
*
|
|
19
|
+
* Alone it is an icon (`size`, 14 inline and 40 as a block). With slot text it
|
|
20
|
+
* becomes a row ("[mark] Loading bundle…"); with `block` it is centred in its
|
|
21
|
+
* box, for a panel or page that is loading. `mono` draws it in the text
|
|
22
|
+
* colour, for a host with its own colours (a busy button).
|
|
23
|
+
*/
|
|
24
|
+
import { computed, useSlots } from "vue";
|
|
25
|
+
import { BRAND_COLOURS, RING_A, RING_B, STAR_A, STAR_B, STAR_BIG, type LogoBrand } from "./logo-paths";
|
|
26
|
+
|
|
27
|
+
const props = withDefaults(defineProps<{
|
|
28
|
+
/** Width and height in px. Default 14, or 40 with `block`. */
|
|
29
|
+
size?: number | undefined;
|
|
30
|
+
/** Accessible name when there is no slot text. */
|
|
31
|
+
label?: string | undefined;
|
|
32
|
+
/** Centre it (and its text, below) in the space it is given. */
|
|
33
|
+
block?: boolean | undefined;
|
|
34
|
+
/** Which drawing; default `logo` for a block or a 24 px+ mark, else `sparkle`. */
|
|
35
|
+
variant?: "logo" | "sparkle" | undefined;
|
|
36
|
+
/** Draw in the current text colour instead of the logo colours. */
|
|
37
|
+
mono?: boolean | undefined;
|
|
38
|
+
/** Hidden from screen readers, for a host that says it is busy itself
|
|
39
|
+
* (a button with `aria-busy`). */
|
|
40
|
+
decorative?: boolean | undefined;
|
|
41
|
+
/** Whose colours: the extension's (default) or the community site's. */
|
|
42
|
+
brand?: LogoBrand | undefined;
|
|
43
|
+
}>(), { size: undefined, label: "Loading", block: false, variant: undefined, mono: false, decorative: false, brand: "extension" });
|
|
44
|
+
|
|
45
|
+
const slots = useSlots();
|
|
46
|
+
const id = `wp-lm-${nextId++}`;
|
|
47
|
+
const px = computed(() => props.size ?? (props.block ? 40 : 14));
|
|
48
|
+
const drawing = computed(() => props.variant ?? (props.block || px.value >= 24 ? "logo" : "sparkle"));
|
|
49
|
+
/** The logo's own colours (brand, not theme tokens: the mark looks the same
|
|
50
|
+
* in light and dark). */
|
|
51
|
+
const VIOLET = computed(() => BRAND_COLOURS[props.brand][0]);
|
|
52
|
+
const INDIGO = computed(() => BRAND_COLOURS[props.brand][1]);
|
|
53
|
+
const TEAL = computed(() => BRAND_COLOURS[props.brand][2]);
|
|
54
|
+
const fill = (colour: string): string => (props.mono ? "currentColor" : colour);
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<span
|
|
59
|
+
class="wp-lm"
|
|
60
|
+
:class="{ 'wp-lm--block': block }"
|
|
61
|
+
:role="decorative ? undefined : 'status'"
|
|
62
|
+
:aria-label="decorative || slots.default ? undefined : label"
|
|
63
|
+
:aria-hidden="decorative || undefined"
|
|
64
|
+
data-test="loading-mark"
|
|
65
|
+
>
|
|
66
|
+
<svg
|
|
67
|
+
class="wp-lm__svg"
|
|
68
|
+
:data-variant="drawing"
|
|
69
|
+
viewBox="0 0 1024 1024"
|
|
70
|
+
:width="px"
|
|
71
|
+
:height="px"
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
>
|
|
74
|
+
<template v-if="drawing === 'logo'">
|
|
75
|
+
<defs v-if="!mono">
|
|
76
|
+
<linearGradient :id="`${id}a`" x1="312.56" y1="897.19" x2="312.56" y2="2" gradientUnits="userSpaceOnUse">
|
|
77
|
+
<stop offset="0" :stop-color="VIOLET" />
|
|
78
|
+
<stop offset="1" :stop-color="INDIGO" />
|
|
79
|
+
</linearGradient>
|
|
80
|
+
<linearGradient :id="`${id}b`" x1="711.44" y1="126.81" x2="711.44" y2="1022" gradientUnits="userSpaceOnUse">
|
|
81
|
+
<stop offset="0" :stop-color="INDIGO" />
|
|
82
|
+
<stop offset="1" :stop-color="TEAL" />
|
|
83
|
+
</linearGradient>
|
|
84
|
+
</defs>
|
|
85
|
+
<path class="wp-lm__ring wp-lm__ring--a" :fill="fill(`url(#${id}a)`)" :d="RING_A" />
|
|
86
|
+
<path class="wp-lm__ring wp-lm__ring--b" :fill="fill(`url(#${id}b)`)" :d="RING_B" />
|
|
87
|
+
<path class="wp-lm__star wp-lm__star--big" :fill="fill(VIOLET)" :d="STAR_BIG" />
|
|
88
|
+
<path class="wp-lm__star wp-lm__star--tw" :fill="fill(INDIGO)" :d="STAR_A" />
|
|
89
|
+
<path class="wp-lm__star wp-lm__star--tw" :fill="fill(TEAL)" :d="STAR_B" />
|
|
90
|
+
</template>
|
|
91
|
+
<g v-else transform="translate(512 512) scale(1.45) translate(-512 -512)">
|
|
92
|
+
<path class="wp-lm__star wp-lm__star--big" :fill="fill(VIOLET)" :d="STAR_BIG" />
|
|
93
|
+
<g class="wp-lm__swap">
|
|
94
|
+
<path class="wp-lm__star" :fill="fill(INDIGO)" :d="STAR_A" />
|
|
95
|
+
<path class="wp-lm__star" :fill="fill(TEAL)" :d="STAR_B" />
|
|
96
|
+
</g>
|
|
97
|
+
</g>
|
|
98
|
+
</svg>
|
|
99
|
+
<span v-if="slots.default" class="wp-lm__text"><slot /></span>
|
|
100
|
+
</span>
|
|
101
|
+
</template>
|
|
102
|
+
|
|
103
|
+
<style scoped>
|
|
104
|
+
.wp-lm {
|
|
105
|
+
--wp-lm-t: 2.4s;
|
|
106
|
+
display: inline-flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
gap: 8px;
|
|
109
|
+
vertical-align: middle;
|
|
110
|
+
}
|
|
111
|
+
.wp-lm--block {
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
gap: 12px;
|
|
116
|
+
width: 100%;
|
|
117
|
+
padding: 48px 16px;
|
|
118
|
+
color: var(--wp-text-muted);
|
|
119
|
+
text-align: center;
|
|
120
|
+
}
|
|
121
|
+
.wp-lm__svg { flex-shrink: 0; overflow: visible; }
|
|
122
|
+
/* Every move animates a registered custom property, never `transform` or
|
|
123
|
+
* `opacity` itself. A browser hands a `transform` / `opacity` animation to
|
|
124
|
+
* the compositor, which lifts the path onto its own layer the moment the
|
|
125
|
+
* animation starts: the layer is rasterised once, at a fixed scale, and
|
|
126
|
+
* snapped to whole pixels, so the crisp mark turned soft and jumped by up to
|
|
127
|
+
* half a pixel as it began to move. A custom property animates on the main
|
|
128
|
+
* thread, so each frame repaints the vector paths in place, as sharp as the
|
|
129
|
+
* mark at rest. The paths are tiny; the repaint costs nothing. */
|
|
130
|
+
.wp-lm__ring { transform-box: view-box; transform-origin: 512px 512px; }
|
|
131
|
+
.wp-lm__star { transform-box: fill-box; transform-origin: center; }
|
|
132
|
+
.wp-lm__ring--a { transform: translate(var(--wp-lm-x), var(--wp-lm-x)); animation: wp-lm-split-a var(--wp-lm-t) cubic-bezier(0.65, 0, 0.35, 1) infinite; }
|
|
133
|
+
.wp-lm__ring--b { transform: translate(var(--wp-lm-x), var(--wp-lm-x)); animation: wp-lm-split-b var(--wp-lm-t) cubic-bezier(0.65, 0, 0.35, 1) infinite; }
|
|
134
|
+
.wp-lm__star--big { transform: rotate(var(--wp-lm-r)) scale(var(--wp-lm-s)); animation: wp-lm-dip var(--wp-lm-t) cubic-bezier(0.65, 0, 0.35, 1) infinite; }
|
|
135
|
+
.wp-lm__star--tw { opacity: var(--wp-lm-o); transform: scale(var(--wp-lm-s)); animation: wp-lm-twinkle var(--wp-lm-t) ease-in-out infinite; }
|
|
136
|
+
.wp-lm__swap {
|
|
137
|
+
transform-box: view-box; transform-origin: 512px 512px;
|
|
138
|
+
transform: rotate(var(--wp-lm-r));
|
|
139
|
+
animation: wp-lm-swap calc(var(--wp-lm-t) * 2) cubic-bezier(0.65, 0, 0.35, 1) infinite;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@property --wp-lm-x { syntax: "<length>"; inherits: false; initial-value: 0px; }
|
|
143
|
+
@property --wp-lm-r { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
|
|
144
|
+
@property --wp-lm-s { syntax: "<number>"; inherits: false; initial-value: 1; }
|
|
145
|
+
@property --wp-lm-o { syntax: "<number>"; inherits: false; initial-value: 1; }
|
|
146
|
+
|
|
147
|
+
@keyframes wp-lm-split-a {
|
|
148
|
+
0%, 15%, 85%, 100% { --wp-lm-x: 0px; }
|
|
149
|
+
40%, 60% { --wp-lm-x: -70px; }
|
|
150
|
+
}
|
|
151
|
+
@keyframes wp-lm-split-b {
|
|
152
|
+
0%, 15%, 85%, 100% { --wp-lm-x: 0px; }
|
|
153
|
+
40%, 60% { --wp-lm-x: 70px; }
|
|
154
|
+
}
|
|
155
|
+
@keyframes wp-lm-dip {
|
|
156
|
+
0%, 15% { --wp-lm-s: 1; --wp-lm-r: 0deg; }
|
|
157
|
+
32% { --wp-lm-s: 0.7; --wp-lm-r: 45deg; }
|
|
158
|
+
50%, 100% { --wp-lm-s: 1; --wp-lm-r: 90deg; }
|
|
159
|
+
}
|
|
160
|
+
@keyframes wp-lm-swap {
|
|
161
|
+
0%, 15% { --wp-lm-r: 0deg; }
|
|
162
|
+
50%, 65% { --wp-lm-r: 180deg; }
|
|
163
|
+
100% { --wp-lm-r: 360deg; }
|
|
164
|
+
}
|
|
165
|
+
@keyframes wp-lm-twinkle {
|
|
166
|
+
0%, 100% { --wp-lm-o: 1; --wp-lm-s: 1; }
|
|
167
|
+
50% { --wp-lm-o: 0.35; --wp-lm-s: 0.6; }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@media (prefers-reduced-motion: reduce) {
|
|
171
|
+
.wp-lm__svg * { animation: none !important; }
|
|
172
|
+
.wp-lm__svg { opacity: var(--wp-lm-o); animation: wp-lm-fade 2.4s ease-in-out infinite; }
|
|
173
|
+
@keyframes wp-lm-fade { 0%, 100% { --wp-lm-o: 1; } 50% { --wp-lm-o: 0.55; } }
|
|
174
|
+
}
|
|
175
|
+
</style>
|
package/src/Logo.vue
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** Gradient ids must be unique on the page, and two logos may wear different
|
|
3
|
-
* brands, so count across every app on it. */
|
|
4
|
-
let nextId = 0;
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<script setup lang="ts">
|
|
8
|
-
/**
|
|
9
|
-
* The Wildcard Pipeline logo, drawn inline so it needs no file. It names
|
|
10
|
-
* itself to screen readers; `decorative` hides it when the name is written
|
|
11
|
-
* beside it (the top bar's brand). `brand="community"` draws it in the
|
|
12
|
-
* community site's colours.
|
|
13
|
-
*
|
|
14
|
-
* <Logo :size="22" decorative />
|
|
15
|
-
*/
|
|
16
|
-
import { computed } from "vue";
|
|
17
|
-
import { BRAND_COLOURS, type LogoBrand } from "./logo-paths";
|
|
18
|
-
|
|
19
|
-
const props = withDefaults(defineProps<{
|
|
20
|
-
size?: number | undefined;
|
|
21
|
-
decorative?: boolean | undefined;
|
|
22
|
-
brand?: LogoBrand | undefined;
|
|
23
|
-
}>(), { size: 28, brand: "extension" });
|
|
24
|
-
|
|
25
|
-
const id = `wp-logo-${nextId++}`;
|
|
26
|
-
const c = computed(() => BRAND_COLOURS[props.brand]);
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
|
-
<template>
|
|
30
|
-
<svg
|
|
31
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
32
|
-
viewBox="0 0 1024 1024"
|
|
33
|
-
:width="size"
|
|
34
|
-
:height="size"
|
|
35
|
-
:role="decorative ? undefined : 'img'"
|
|
36
|
-
:aria-label="decorative ? undefined : 'Wildcard Pipeline'"
|
|
37
|
-
:aria-hidden="decorative || undefined"
|
|
38
|
-
class="wp-logo"
|
|
39
|
-
>
|
|
40
|
-
<defs>
|
|
41
|
-
<linearGradient :id="`${id}a`" x1="312.56" y1="897.19" x2="312.56" y2="2" gradientUnits="userSpaceOnUse">
|
|
42
|
-
<stop offset="0" :stop-color="c[0]" />
|
|
43
|
-
<stop offset="1" :stop-color="c[1]" />
|
|
44
|
-
</linearGradient>
|
|
45
|
-
<linearGradient :id="`${id}b`" x1="711.44" y1="126.81" x2="711.44" y2="1022" gradientUnits="userSpaceOnUse">
|
|
46
|
-
<stop offset="0" :stop-color="c[1]" />
|
|
47
|
-
<stop offset="1" :stop-color="c[2]" />
|
|
48
|
-
</linearGradient>
|
|
49
|
-
</defs>
|
|
50
|
-
<path :fill="`url(#${id}a)`" d="m512,2c-53.6,0-98.47,38.16-108.84,88.73-75.07,19.29-143.32,58.26-198.74,113.68-55.42,55.42-94.39,123.68-113.68,198.74-50.58,10.38-88.73,55.24-88.73,108.84s38.16,98.47,88.73,108.84c13.62,53.14,37.03,102.64,69.77,147.47,37.14,50.86,85.83,93.86,140.81,124.33,5.54,3.08,11.56,4.54,17.48,4.54,12.7,0,25.02-6.72,31.61-18.61,9.68-17.44,3.38-39.42-14.07-49.09-85.32-47.32-147.22-125.71-173.38-218.21,36.3-18.29,61.27-55.92,61.27-99.28s-24.94-80.97-61.23-99.27c34.22-119.96,129.79-215.52,249.74-249.74,18.3,36.29,55.92,61.23,99.27,61.23,61.27,0,111.11-49.84,111.11-111.11S573.27,2,512,2ZM113.11,473.11c21.44,0,38.89,17.44,38.89,38.89,0,20.1-15.32,36.69-34.91,38.69h-.01c-.18.02-.36.03-.53.04-1.13.1-2.28.16-3.43.16-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89Zm398.89-321.11c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Z" />
|
|
51
|
-
<path :fill="`url(#${id}b)`" d="m933.27,403.16c-13.62-53.14-37.03-102.64-69.77-147.47-37.14-50.86-85.83-93.86-140.81-124.33-17.44-9.68-39.42-3.38-49.09,14.07-9.68,17.44-3.38,39.42,14.07,49.09,85.32,47.32,147.22,125.71,173.38,218.21-36.3,18.29-61.27,55.92-61.27,99.28s24.94,80.97,61.23,99.27c-34.22,119.96-129.79,215.52-249.74,249.74-18.3-36.29-55.92-61.23-99.27-61.23-61.27,0-111.11,49.84-111.11,111.11s49.84,111.11,111.11,111.11c53.6,0,98.47-38.16,108.84-88.73,75.07-19.29,143.32-58.26,198.74-113.68,55.42-55.42,94.39-123.68,113.68-198.74,50.58-10.38,88.73-55.24,88.73-108.84s-38.16-98.47-88.73-108.84Zm-421.27,546.62c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Zm398.89-398.89c-21.44,0-38.89-17.44-38.89-38.89,0-20.03,15.22-36.58,34.72-38.67.38-.04.77-.08,1.16-.11h.02c.99-.08,1.99-.11,2.99-.11,21.44,0,38.89,17.44,38.89,38.89s-17.44,38.89-38.89,38.89Z" />
|
|
52
|
-
<path :fill="c[0]" d="m735.02,517.56c-153.82,30.87-186.59,63.64-217.46,217.46-1.22,6.08-9.9,6.08-11.12,0-30.87-153.82-63.64-186.59-217.46-217.46-6.08-1.22-6.08-9.9,0-11.12,153.82-30.87,186.59-63.64,217.46-217.46,1.22-6.08,9.9-6.08,11.12,0,30.87,153.82,63.64,186.59,217.46,217.46,6.08,1.22,6.08,9.9,0,11.12Z" />
|
|
53
|
-
<path :fill="c[1]" d="m434.06,665.85c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z" />
|
|
54
|
-
<path :fill="c[2]" d="m738.07,361.84c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z" />
|
|
55
|
-
</svg>
|
|
56
|
-
</template>
|
|
57
|
-
|
|
58
|
-
<style scoped>
|
|
59
|
-
.wp-logo { display: block; flex-shrink: 0; }
|
|
60
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** Gradient ids must be unique on the page, and two logos may wear different
|
|
3
|
+
* brands, so count across every app on it. */
|
|
4
|
+
let nextId = 0;
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
/**
|
|
9
|
+
* The Wildcard Pipeline logo, drawn inline so it needs no file. It names
|
|
10
|
+
* itself to screen readers; `decorative` hides it when the name is written
|
|
11
|
+
* beside it (the top bar's brand). `brand="community"` draws it in the
|
|
12
|
+
* community site's colours.
|
|
13
|
+
*
|
|
14
|
+
* <Logo :size="22" decorative />
|
|
15
|
+
*/
|
|
16
|
+
import { computed } from "vue";
|
|
17
|
+
import { BRAND_COLOURS, type LogoBrand } from "./logo-paths";
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(defineProps<{
|
|
20
|
+
size?: number | undefined;
|
|
21
|
+
decorative?: boolean | undefined;
|
|
22
|
+
brand?: LogoBrand | undefined;
|
|
23
|
+
}>(), { size: 28, brand: "extension" });
|
|
24
|
+
|
|
25
|
+
const id = `wp-logo-${nextId++}`;
|
|
26
|
+
const c = computed(() => BRAND_COLOURS[props.brand]);
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<svg
|
|
31
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
32
|
+
viewBox="0 0 1024 1024"
|
|
33
|
+
:width="size"
|
|
34
|
+
:height="size"
|
|
35
|
+
:role="decorative ? undefined : 'img'"
|
|
36
|
+
:aria-label="decorative ? undefined : 'Wildcard Pipeline'"
|
|
37
|
+
:aria-hidden="decorative || undefined"
|
|
38
|
+
class="wp-logo"
|
|
39
|
+
>
|
|
40
|
+
<defs>
|
|
41
|
+
<linearGradient :id="`${id}a`" x1="312.56" y1="897.19" x2="312.56" y2="2" gradientUnits="userSpaceOnUse">
|
|
42
|
+
<stop offset="0" :stop-color="c[0]" />
|
|
43
|
+
<stop offset="1" :stop-color="c[1]" />
|
|
44
|
+
</linearGradient>
|
|
45
|
+
<linearGradient :id="`${id}b`" x1="711.44" y1="126.81" x2="711.44" y2="1022" gradientUnits="userSpaceOnUse">
|
|
46
|
+
<stop offset="0" :stop-color="c[1]" />
|
|
47
|
+
<stop offset="1" :stop-color="c[2]" />
|
|
48
|
+
</linearGradient>
|
|
49
|
+
</defs>
|
|
50
|
+
<path :fill="`url(#${id}a)`" d="m512,2c-53.6,0-98.47,38.16-108.84,88.73-75.07,19.29-143.32,58.26-198.74,113.68-55.42,55.42-94.39,123.68-113.68,198.74-50.58,10.38-88.73,55.24-88.73,108.84s38.16,98.47,88.73,108.84c13.62,53.14,37.03,102.64,69.77,147.47,37.14,50.86,85.83,93.86,140.81,124.33,5.54,3.08,11.56,4.54,17.48,4.54,12.7,0,25.02-6.72,31.61-18.61,9.68-17.44,3.38-39.42-14.07-49.09-85.32-47.32-147.22-125.71-173.38-218.21,36.3-18.29,61.27-55.92,61.27-99.28s-24.94-80.97-61.23-99.27c34.22-119.96,129.79-215.52,249.74-249.74,18.3,36.29,55.92,61.23,99.27,61.23,61.27,0,111.11-49.84,111.11-111.11S573.27,2,512,2ZM113.11,473.11c21.44,0,38.89,17.44,38.89,38.89,0,20.1-15.32,36.69-34.91,38.69h-.01c-.18.02-.36.03-.53.04-1.13.1-2.28.16-3.43.16-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89Zm398.89-321.11c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Z" />
|
|
51
|
+
<path :fill="`url(#${id}b)`" d="m933.27,403.16c-13.62-53.14-37.03-102.64-69.77-147.47-37.14-50.86-85.83-93.86-140.81-124.33-17.44-9.68-39.42-3.38-49.09,14.07-9.68,17.44-3.38,39.42,14.07,49.09,85.32,47.32,147.22,125.71,173.38,218.21-36.3,18.29-61.27,55.92-61.27,99.28s24.94,80.97,61.23,99.27c-34.22,119.96-129.79,215.52-249.74,249.74-18.3-36.29-55.92-61.23-99.27-61.23-61.27,0-111.11,49.84-111.11,111.11s49.84,111.11,111.11,111.11c53.6,0,98.47-38.16,108.84-88.73,75.07-19.29,143.32-58.26,198.74-113.68,55.42-55.42,94.39-123.68,113.68-198.74,50.58-10.38,88.73-55.24,88.73-108.84s-38.16-98.47-88.73-108.84Zm-421.27,546.62c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Zm398.89-398.89c-21.44,0-38.89-17.44-38.89-38.89,0-20.03,15.22-36.58,34.72-38.67.38-.04.77-.08,1.16-.11h.02c.99-.08,1.99-.11,2.99-.11,21.44,0,38.89,17.44,38.89,38.89s-17.44,38.89-38.89,38.89Z" />
|
|
52
|
+
<path :fill="c[0]" d="m735.02,517.56c-153.82,30.87-186.59,63.64-217.46,217.46-1.22,6.08-9.9,6.08-11.12,0-30.87-153.82-63.64-186.59-217.46-217.46-6.08-1.22-6.08-9.9,0-11.12,153.82-30.87,186.59-63.64,217.46-217.46,1.22-6.08,9.9-6.08,11.12,0,30.87,153.82,63.64,186.59,217.46,217.46,6.08,1.22,6.08,9.9,0,11.12Z" />
|
|
53
|
+
<path :fill="c[1]" d="m434.06,665.85c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z" />
|
|
54
|
+
<path :fill="c[2]" d="m738.07,361.84c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z" />
|
|
55
|
+
</svg>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style scoped>
|
|
59
|
+
.wp-logo { display: block; flex-shrink: 0; }
|
|
60
|
+
</style>
|
package/src/Modal.vue
CHANGED
|
@@ -1,76 +1,78 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import CloseButton from "./CloseButton.vue";
|
|
3
|
-
import { computed, ref, useId, useSlots } from "vue";
|
|
4
|
-
import { useDialogFocus } from "./useDialogFocus";
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
open: boolean;
|
|
8
|
-
title?: string | undefined;
|
|
9
|
-
size?: "sm" | "md" | "lg" | undefined;
|
|
10
|
-
closeOnBackdrop?: boolean | undefined;
|
|
11
|
-
}
|
|
12
|
-
const props = withDefaults(defineProps<Props>(), { size: "md", closeOnBackdrop: true });
|
|
13
|
-
|
|
14
|
-
const emit = defineEmits<{
|
|
15
|
-
(e: "update:open", v: boolean): void;
|
|
16
|
-
}>();
|
|
17
|
-
|
|
18
|
-
const slots = useSlots();
|
|
19
|
-
|
|
20
|
-
const modalClasses = computed(() => ["wp-modal", `wp-modal--${props.size}`]);
|
|
21
|
-
const dialog = ref<HTMLElement | null>(null);
|
|
22
|
-
const titleId = `wp-modal-title-${useId()}`;
|
|
23
|
-
|
|
24
|
-
function close() {
|
|
25
|
-
emit("update:open", false);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function onBackdrop() {
|
|
29
|
-
if (props.closeOnBackdrop) close();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
useDialogFocus(dialog, () => props.open, {
|
|
33
|
-
onEscape: close,
|
|
34
|
-
// The first control in the body, else the dialog itself (the close button
|
|
35
|
-
// would make Enter close it).
|
|
36
|
-
initial: (root, els) => {
|
|
37
|
-
const body = root.querySelector(".wp-modal__body");
|
|
38
|
-
return els.find((el) => body?.contains(el)) ?? root;
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
|
-
<template>
|
|
44
|
-
<Teleport to="body">
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import CloseButton from "./CloseButton.vue";
|
|
3
|
+
import { computed, ref, useId, useSlots } from "vue";
|
|
4
|
+
import { useDialogFocus } from "./useDialogFocus";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
open: boolean;
|
|
8
|
+
title?: string | undefined;
|
|
9
|
+
size?: "sm" | "md" | "lg" | undefined;
|
|
10
|
+
closeOnBackdrop?: boolean | undefined;
|
|
11
|
+
}
|
|
12
|
+
const props = withDefaults(defineProps<Props>(), { size: "md", closeOnBackdrop: true });
|
|
13
|
+
|
|
14
|
+
const emit = defineEmits<{
|
|
15
|
+
(e: "update:open", v: boolean): void;
|
|
16
|
+
}>();
|
|
17
|
+
|
|
18
|
+
const slots = useSlots();
|
|
19
|
+
|
|
20
|
+
const modalClasses = computed(() => ["wp-modal", `wp-modal--${props.size}`]);
|
|
21
|
+
const dialog = ref<HTMLElement | null>(null);
|
|
22
|
+
const titleId = `wp-modal-title-${useId()}`;
|
|
23
|
+
|
|
24
|
+
function close() {
|
|
25
|
+
emit("update:open", false);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function onBackdrop() {
|
|
29
|
+
if (props.closeOnBackdrop) close();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
useDialogFocus(dialog, () => props.open, {
|
|
33
|
+
onEscape: close,
|
|
34
|
+
// The first control in the body, else the dialog itself (the close button
|
|
35
|
+
// would make Enter close it).
|
|
36
|
+
initial: (root, els) => {
|
|
37
|
+
const body = root.querySelector(".wp-modal__body");
|
|
38
|
+
return els.find((el) => body?.contains(el)) ?? root;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<Teleport to="body">
|
|
45
|
+
<!-- The backdrop is a click target only; keyboard users close with Escape. -->
|
|
46
|
+
<!-- eslint-disable-next-line vuejs-accessibility/no-static-element-interactions -->
|
|
47
|
+
<div
|
|
48
|
+
v-if="open"
|
|
49
|
+
class="wp-modal__backdrop"
|
|
50
|
+
data-test="modal-backdrop"
|
|
51
|
+
@mousedown.self="onBackdrop"
|
|
52
|
+
>
|
|
53
|
+
<div
|
|
54
|
+
ref="dialog"
|
|
55
|
+
:class="modalClasses"
|
|
56
|
+
role="dialog"
|
|
57
|
+
aria-modal="true"
|
|
58
|
+
:aria-labelledby="title || slots.header ? titleId : undefined"
|
|
59
|
+
tabindex="-1"
|
|
60
|
+
@mousedown.stop
|
|
61
|
+
>
|
|
62
|
+
<header v-if="title || slots.header" class="wp-modal__head">
|
|
63
|
+
<h3 v-if="title" :id="titleId" class="wp-modal__title">{{ title }}</h3>
|
|
64
|
+
<!-- A custom header puts :id="titleId" on its own heading. -->
|
|
65
|
+
<slot name="header" :title-id="titleId" />
|
|
66
|
+
<span class="wp-spacer" />
|
|
67
|
+
<CloseButton kbd="Esc" @click="close" />
|
|
68
|
+
</header>
|
|
69
|
+
<div class="wp-modal__body">
|
|
70
|
+
<slot />
|
|
71
|
+
</div>
|
|
72
|
+
<footer v-if="slots.footer" class="wp-modal__foot">
|
|
73
|
+
<slot name="footer" />
|
|
74
|
+
</footer>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</Teleport>
|
|
78
|
+
</template>
|