@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/icon-catalog.ts
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Curated PrimeIcons glyphs offered by `IconPicker.vue`.
|
|
3
|
-
*
|
|
4
|
-
* PrimeIcons ships ~314 glyphs; this is a subset. The full set would need its
|
|
5
|
-
* own search UI, and most of it (chevrons, spinners, media transport, brand
|
|
6
|
-
* logos) means nothing as a library label. The groups below follow the kinds
|
|
7
|
-
* of things people actually categorise prompt modules by.
|
|
8
|
-
*
|
|
9
|
-
* Names are PrimeIcons ids MINUS the `pi-` prefix (`"user"`, not `"pi-user"`)
|
|
10
|
-
* — that is what the engine's `icon` column holds and what `Icon.vue` expects.
|
|
11
|
-
*
|
|
12
|
-
* In a plain module rather than inside the SFC so `icon-catalog.test.ts` can
|
|
13
|
-
* check every name against the installed `primeicons.css` without mounting a
|
|
14
|
-
* component. That test is the point: a name with no matching glyph renders as
|
|
15
|
-
* an EMPTY CELL, which looks like a layout bug rather than a typo, and `brush`
|
|
16
|
-
* shipped that way because nothing verified the list.
|
|
17
|
-
*/
|
|
18
|
-
export interface IconGroup {
|
|
19
|
-
label: string;
|
|
20
|
-
icons: string[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const ICON_GROUPS: IconGroup[] = [
|
|
24
|
-
{
|
|
25
|
-
label: "Subject",
|
|
26
|
-
icons: ["user", "users", "heart", "star", "eye", "face-smile", "crown", "id-card"],
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
// `sliders-h` stands in for the art-supply glyph PrimeIcons doesn't have —
|
|
30
|
-
// there is no brush/paint icon in the set, and `palette` already carries
|
|
31
|
-
// "art style". Sliders read as "tune the look".
|
|
32
|
-
label: "Style & look",
|
|
33
|
-
icons: ["palette", "pencil", "sliders-h", "camera", "image", "sparkles", "sun", "moon"],
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
label: "Scene",
|
|
37
|
-
icons: ["home", "building", "map", "map-marker", "globe", "compass", "car", "cloud"],
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
label: "Structure",
|
|
41
|
-
icons: ["box", "folder", "book", "tag", "tags", "list", "sitemap", "objects-column"],
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
label: "Signals",
|
|
45
|
-
icons: ["bolt", "flag", "bell", "shield", "lock", "wrench", "filter", "info-circle"],
|
|
46
|
-
},
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
/** Every offered glyph, flattened — for tests and any future search box. */
|
|
50
|
-
export function allCuratedIcons(): string[] {
|
|
51
|
-
return ICON_GROUPS.flatMap((g) => g.icons);
|
|
52
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Curated PrimeIcons glyphs offered by `IconPicker.vue`.
|
|
3
|
+
*
|
|
4
|
+
* PrimeIcons ships ~314 glyphs; this is a subset. The full set would need its
|
|
5
|
+
* own search UI, and most of it (chevrons, spinners, media transport, brand
|
|
6
|
+
* logos) means nothing as a library label. The groups below follow the kinds
|
|
7
|
+
* of things people actually categorise prompt modules by.
|
|
8
|
+
*
|
|
9
|
+
* Names are PrimeIcons ids MINUS the `pi-` prefix (`"user"`, not `"pi-user"`)
|
|
10
|
+
* — that is what the engine's `icon` column holds and what `Icon.vue` expects.
|
|
11
|
+
*
|
|
12
|
+
* In a plain module rather than inside the SFC so `icon-catalog.test.ts` can
|
|
13
|
+
* check every name against the installed `primeicons.css` without mounting a
|
|
14
|
+
* component. That test is the point: a name with no matching glyph renders as
|
|
15
|
+
* an EMPTY CELL, which looks like a layout bug rather than a typo, and `brush`
|
|
16
|
+
* shipped that way because nothing verified the list.
|
|
17
|
+
*/
|
|
18
|
+
export interface IconGroup {
|
|
19
|
+
label: string;
|
|
20
|
+
icons: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const ICON_GROUPS: IconGroup[] = [
|
|
24
|
+
{
|
|
25
|
+
label: "Subject",
|
|
26
|
+
icons: ["user", "users", "heart", "star", "eye", "face-smile", "crown", "id-card"],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
// `sliders-h` stands in for the art-supply glyph PrimeIcons doesn't have —
|
|
30
|
+
// there is no brush/paint icon in the set, and `palette` already carries
|
|
31
|
+
// "art style". Sliders read as "tune the look".
|
|
32
|
+
label: "Style & look",
|
|
33
|
+
icons: ["palette", "pencil", "sliders-h", "camera", "image", "sparkles", "sun", "moon"],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: "Scene",
|
|
37
|
+
icons: ["home", "building", "map", "map-marker", "globe", "compass", "car", "cloud"],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: "Structure",
|
|
41
|
+
icons: ["box", "folder", "book", "tag", "tags", "list", "sitemap", "objects-column"],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: "Signals",
|
|
45
|
+
icons: ["bolt", "flag", "bell", "shield", "lock", "wrench", "filter", "info-circle"],
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/** Every offered glyph, flattened — for tests and any future search box. */
|
|
50
|
+
export function allCuratedIcons(): string[] {
|
|
51
|
+
return ICON_GROUPS.flatMap((g) => g.icons);
|
|
52
|
+
}
|
package/src/image-zoom.ts
CHANGED
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The maths behind `WpImageZoom`: one transform, `translate(x, y) scale(s)`
|
|
3
|
-
* with its origin at the image's top-left corner. Pure, so it is tested on
|
|
4
|
-
* its own.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export interface Size { width: number; height: number }
|
|
8
|
-
export interface View { scale: number; x: number; y: number }
|
|
9
|
-
|
|
10
|
-
/** Zoom never goes below this share of Fit (or of 100% for a tiny image), nor above MAX_SCALE. */
|
|
11
|
-
export const MAX_SCALE = 8;
|
|
12
|
-
/** The steps the − / + buttons jump through, as real-size factors. */
|
|
13
|
-
export const ZOOM_STEPS = [0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 6, 8] as const;
|
|
14
|
-
/** How long `WpImageZoom`'s bar stays after the last pointer move or key, with `autohide`. */
|
|
15
|
-
export const IDLE_MS = 2000;
|
|
16
|
-
/** One wheel notch. */
|
|
17
|
-
export const WHEEL_FACTOR = 1.2;
|
|
18
|
-
|
|
19
|
-
/** The scale that shows the whole image in the stage (it may be above 1 for a small image). */
|
|
20
|
-
export function fitScale(image: Size, stage: Size): number {
|
|
21
|
-
if (image.width <= 0 || image.height <= 0 || stage.width <= 0 || stage.height <= 0) return 1;
|
|
22
|
-
return Math.min(stage.width / image.width, stage.height / image.height);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function minScale(image: Size, stage: Size): number {
|
|
26
|
-
return Math.min(fitScale(image, stage), 1) / 2;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function clampScale(scale: number, image: Size, stage: Size): number {
|
|
30
|
-
return Math.min(MAX_SCALE, Math.max(minScale(image, stage), scale));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Keep the image in view: centred on an axis where it is smaller than the
|
|
34
|
-
* stage, otherwise never showing an empty edge. */
|
|
35
|
-
export function clampView(view: View, image: Size, stage: Size): View {
|
|
36
|
-
const axis = (pos: number, img: number, room: number): number => {
|
|
37
|
-
const size = img * view.scale;
|
|
38
|
-
if (size <= room) return (room - size) / 2;
|
|
39
|
-
return Math.min(0, Math.max(room - size, pos));
|
|
40
|
-
};
|
|
41
|
-
return { scale: view.scale, x: axis(view.x, image.width, stage.width), y: axis(view.y, image.height, stage.height) };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Set the scale, keeping the image point under `at` (stage px) where it is. */
|
|
45
|
-
export function zoomAt(view: View, scale: number, at: { x: number; y: number }, image: Size, stage: Size): View {
|
|
46
|
-
const s = clampScale(scale, image, stage);
|
|
47
|
-
const k = s / view.scale;
|
|
48
|
-
return clampView({ scale: s, x: at.x - (at.x - view.x) * k, y: at.y - (at.y - view.y) * k }, image, stage);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** The view at `scale` centred on the image point `centre` (fractions 0..1). */
|
|
52
|
-
export function viewAround(scale: number, centre: { x: number; y: number }, image: Size, stage: Size): View {
|
|
53
|
-
const s = clampScale(scale, image, stage);
|
|
54
|
-
return clampView({
|
|
55
|
-
scale: s,
|
|
56
|
-
x: stage.width / 2 - centre.x * image.width * s,
|
|
57
|
-
y: stage.height / 2 - centre.y * image.height * s,
|
|
58
|
-
}, image, stage);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** The image point (fractions 0..1) at the middle of the stage. */
|
|
62
|
-
export function centreOf(view: View, image: Size, stage: Size): { x: number; y: number } {
|
|
63
|
-
if (image.width <= 0 || image.height <= 0) return { x: 0.5, y: 0.5 };
|
|
64
|
-
const f = (mid: number, pos: number, img: number): number => Math.min(1, Math.max(0, (mid - pos) / (img * view.scale)));
|
|
65
|
-
return { x: f(stage.width / 2, view.x, image.width), y: f(stage.height / 2, view.y, image.height) };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** The next step up (dir 1) or down (dir -1) from `scale`, with Fit slotted in among the steps. */
|
|
69
|
-
export function stepScale(scale: number, dir: 1 | -1, fit: number): number {
|
|
70
|
-
const steps = [...ZOOM_STEPS, fit].sort((a, b) => a - b);
|
|
71
|
-
const eps = 1e-3;
|
|
72
|
-
if (dir > 0) return steps.find((s) => s > scale + eps) ?? MAX_SCALE;
|
|
73
|
-
return [...steps].reverse().find((s) => s < scale - eps) ?? steps[0];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** "100%", "35%", "250%". */
|
|
77
|
-
export function percent(scale: number): string {
|
|
78
|
-
return `${Math.round(scale * 100)}%`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** The part of the image the stage shows, as fractions 0..1 of the image (for a mini-map). */
|
|
82
|
-
export function visibleRect(view: View, image: Size, stage: Size): { x: number; y: number; w: number; h: number } {
|
|
83
|
-
if (image.width <= 0 || image.height <= 0) return { x: 0, y: 0, w: 1, h: 1 };
|
|
84
|
-
const axis = (pos: number, img: number, room: number): [number, number] => {
|
|
85
|
-
const size = img * view.scale;
|
|
86
|
-
const a = Math.min(1, Math.max(0, -pos / size));
|
|
87
|
-
const b = Math.min(1, Math.max(0, (room - pos) / size));
|
|
88
|
-
return [a, b - a];
|
|
89
|
-
};
|
|
90
|
-
const [x, w] = axis(view.x, image.width, stage.width);
|
|
91
|
-
const [y, h] = axis(view.y, image.height, stage.height);
|
|
92
|
-
return { x, y, w, h };
|
|
93
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* The maths behind `WpImageZoom`: one transform, `translate(x, y) scale(s)`
|
|
3
|
+
* with its origin at the image's top-left corner. Pure, so it is tested on
|
|
4
|
+
* its own.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface Size { width: number; height: number }
|
|
8
|
+
export interface View { scale: number; x: number; y: number }
|
|
9
|
+
|
|
10
|
+
/** Zoom never goes below this share of Fit (or of 100% for a tiny image), nor above MAX_SCALE. */
|
|
11
|
+
export const MAX_SCALE = 8;
|
|
12
|
+
/** The steps the − / + buttons jump through, as real-size factors. */
|
|
13
|
+
export const ZOOM_STEPS = [0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 6, 8] as const;
|
|
14
|
+
/** How long `WpImageZoom`'s bar stays after the last pointer move or key, with `autohide`. */
|
|
15
|
+
export const IDLE_MS = 2000;
|
|
16
|
+
/** One wheel notch. */
|
|
17
|
+
export const WHEEL_FACTOR = 1.2;
|
|
18
|
+
|
|
19
|
+
/** The scale that shows the whole image in the stage (it may be above 1 for a small image). */
|
|
20
|
+
export function fitScale(image: Size, stage: Size): number {
|
|
21
|
+
if (image.width <= 0 || image.height <= 0 || stage.width <= 0 || stage.height <= 0) return 1;
|
|
22
|
+
return Math.min(stage.width / image.width, stage.height / image.height);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function minScale(image: Size, stage: Size): number {
|
|
26
|
+
return Math.min(fitScale(image, stage), 1) / 2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function clampScale(scale: number, image: Size, stage: Size): number {
|
|
30
|
+
return Math.min(MAX_SCALE, Math.max(minScale(image, stage), scale));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Keep the image in view: centred on an axis where it is smaller than the
|
|
34
|
+
* stage, otherwise never showing an empty edge. */
|
|
35
|
+
export function clampView(view: View, image: Size, stage: Size): View {
|
|
36
|
+
const axis = (pos: number, img: number, room: number): number => {
|
|
37
|
+
const size = img * view.scale;
|
|
38
|
+
if (size <= room) return (room - size) / 2;
|
|
39
|
+
return Math.min(0, Math.max(room - size, pos));
|
|
40
|
+
};
|
|
41
|
+
return { scale: view.scale, x: axis(view.x, image.width, stage.width), y: axis(view.y, image.height, stage.height) };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Set the scale, keeping the image point under `at` (stage px) where it is. */
|
|
45
|
+
export function zoomAt(view: View, scale: number, at: { x: number; y: number }, image: Size, stage: Size): View {
|
|
46
|
+
const s = clampScale(scale, image, stage);
|
|
47
|
+
const k = s / view.scale;
|
|
48
|
+
return clampView({ scale: s, x: at.x - (at.x - view.x) * k, y: at.y - (at.y - view.y) * k }, image, stage);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The view at `scale` centred on the image point `centre` (fractions 0..1). */
|
|
52
|
+
export function viewAround(scale: number, centre: { x: number; y: number }, image: Size, stage: Size): View {
|
|
53
|
+
const s = clampScale(scale, image, stage);
|
|
54
|
+
return clampView({
|
|
55
|
+
scale: s,
|
|
56
|
+
x: stage.width / 2 - centre.x * image.width * s,
|
|
57
|
+
y: stage.height / 2 - centre.y * image.height * s,
|
|
58
|
+
}, image, stage);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The image point (fractions 0..1) at the middle of the stage. */
|
|
62
|
+
export function centreOf(view: View, image: Size, stage: Size): { x: number; y: number } {
|
|
63
|
+
if (image.width <= 0 || image.height <= 0) return { x: 0.5, y: 0.5 };
|
|
64
|
+
const f = (mid: number, pos: number, img: number): number => Math.min(1, Math.max(0, (mid - pos) / (img * view.scale)));
|
|
65
|
+
return { x: f(stage.width / 2, view.x, image.width), y: f(stage.height / 2, view.y, image.height) };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The next step up (dir 1) or down (dir -1) from `scale`, with Fit slotted in among the steps. */
|
|
69
|
+
export function stepScale(scale: number, dir: 1 | -1, fit: number): number {
|
|
70
|
+
const steps = [...ZOOM_STEPS, fit].sort((a, b) => a - b);
|
|
71
|
+
const eps = 1e-3;
|
|
72
|
+
if (dir > 0) return steps.find((s) => s > scale + eps) ?? MAX_SCALE;
|
|
73
|
+
return [...steps].reverse().find((s) => s < scale - eps) ?? steps[0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** "100%", "35%", "250%". */
|
|
77
|
+
export function percent(scale: number): string {
|
|
78
|
+
return `${Math.round(scale * 100)}%`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The part of the image the stage shows, as fractions 0..1 of the image (for a mini-map). */
|
|
82
|
+
export function visibleRect(view: View, image: Size, stage: Size): { x: number; y: number; w: number; h: number } {
|
|
83
|
+
if (image.width <= 0 || image.height <= 0) return { x: 0, y: 0, w: 1, h: 1 };
|
|
84
|
+
const axis = (pos: number, img: number, room: number): [number, number] => {
|
|
85
|
+
const size = img * view.scale;
|
|
86
|
+
const a = Math.min(1, Math.max(0, -pos / size));
|
|
87
|
+
const b = Math.min(1, Math.max(0, (room - pos) / size));
|
|
88
|
+
return [a, b - a];
|
|
89
|
+
};
|
|
90
|
+
const [x, w] = axis(view.x, image.width, stage.width);
|
|
91
|
+
const [y, h] = axis(view.y, image.height, stage.height);
|
|
92
|
+
return { x, y, w, h };
|
|
93
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The colour of each module type, for a Chip (or anything else) that names
|
|
3
|
+
* one: the `--wp-kind-*` colour the extension uses for that type, bundles
|
|
4
|
+
* slate. There is no kind chip: a kind is a Chip with this colour.
|
|
5
|
+
*
|
|
6
|
+
* <Chip :color="kindColour(itemType(post.kind, post.subtype))">{{ itemType(post.kind, post.subtype) }}</Chip>
|
|
7
|
+
*/
|
|
8
|
+
export type ModuleSubtype = "fixed_values" | "combine" | "wildcard" | "constraint" | "derivation";
|
|
9
|
+
export type ItemType = ModuleSubtype | "bundle";
|
|
10
|
+
|
|
11
|
+
const COLOURS: Record<ItemType, string> = {
|
|
12
|
+
fixed_values: "var(--wp-kind-fixed)",
|
|
13
|
+
combine: "var(--wp-kind-combine)",
|
|
14
|
+
wildcard: "var(--wp-kind-wildcard)",
|
|
15
|
+
constraint: "var(--wp-kind-constraint)",
|
|
16
|
+
derivation: "var(--wp-kind-derivation)",
|
|
17
|
+
bundle: "var(--wp-bundle-default)",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** A post or library item's type. A module with no subtype reads as
|
|
21
|
+
* `fixed_values` (the oldest type); a `"bundle"` subtype reads as a bundle. */
|
|
22
|
+
export function itemType(kind: "module" | "bundle", subtype?: ItemType | null): ItemType {
|
|
23
|
+
return kind === "bundle" ? "bundle" : (subtype ?? "fixed_values");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function kindColour(type: ItemType): string {
|
|
27
|
+
return COLOURS[type];
|
|
28
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoadingMark's motion, drawn on a canvas.
|
|
3
|
+
*
|
|
4
|
+
* The mark used to move its SVG paths with CSS. A keyframed `transform` /
|
|
5
|
+
* `opacity` goes to the compositor, which blurs the path and snaps it to whole
|
|
6
|
+
* pixels as it starts; animating registered custom properties fixed that in
|
|
7
|
+
* Chromium, but Firefox still lays each frame of an SVG transform on the pixel
|
|
8
|
+
* grid, so the rings stepped and twitched as they slid. A canvas puts every
|
|
9
|
+
* shape at its exact fractional position each frame, sharp and smooth in both.
|
|
10
|
+
*
|
|
11
|
+
* One `requestAnimationFrame` loop draws every mark on the page. Each mark
|
|
12
|
+
* keeps its own clock, so a host can slow or pause the marks inside it with
|
|
13
|
+
* `data-wp-motion-speed` (a number) and `data-wp-motion-paused` on an
|
|
14
|
+
* ancestor, as the kit site's Animations section does.
|
|
15
|
+
*/
|
|
16
|
+
import { RING_A, RING_B, STAR_A, STAR_B, STAR_BIG } from "./logo-paths";
|
|
17
|
+
|
|
18
|
+
/** One loop of the logo, in ms; the sparkle's swap takes two. */
|
|
19
|
+
export const LOOP_MS = 2400;
|
|
20
|
+
/** Room around the 1024 box for the rings as they slide apart (70 units)
|
|
21
|
+
* and for anti-aliasing at the edge. */
|
|
22
|
+
export const PAD = 82;
|
|
23
|
+
export const BOX = 1024 + 2 * PAD;
|
|
24
|
+
|
|
25
|
+
/** CSS `cubic-bezier(x1, y1, x2, y2)` as a function of progress. */
|
|
26
|
+
export function cubicBezier(x1: number, y1: number, x2: number, y2: number): (x: number) => number {
|
|
27
|
+
return (x) => {
|
|
28
|
+
if (x <= 0) return 0;
|
|
29
|
+
if (x >= 1) return 1;
|
|
30
|
+
let lo = 0;
|
|
31
|
+
let hi = 1;
|
|
32
|
+
let t = x;
|
|
33
|
+
for (let k = 0; k < 30; k++) {
|
|
34
|
+
t = (lo + hi) / 2;
|
|
35
|
+
const cx = 3 * x1 * t * (1 - t) ** 2 + 3 * x2 * t * t * (1 - t) + t ** 3;
|
|
36
|
+
if (cx < x) lo = t;
|
|
37
|
+
else hi = t;
|
|
38
|
+
}
|
|
39
|
+
return 3 * y1 * t * (1 - t) ** 2 + 3 * y2 * t * t * (1 - t) + t ** 3;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const SMOOTH = cubicBezier(0.65, 0, 0.35, 1);
|
|
44
|
+
const EASE_IN_OUT = cubicBezier(0.42, 0, 0.58, 1);
|
|
45
|
+
|
|
46
|
+
type Stop = readonly [at: number, values: readonly number[]];
|
|
47
|
+
|
|
48
|
+
/** Value of a keyframe track at progress `p` (0..1), eased per segment the
|
|
49
|
+
* way CSS eases between two keyframes. */
|
|
50
|
+
export function track(stops: readonly Stop[], p: number, ease: (x: number) => number): number[] {
|
|
51
|
+
for (let k = 0; k < stops.length - 1; k++) {
|
|
52
|
+
const a = stops[k];
|
|
53
|
+
const b = stops[k + 1];
|
|
54
|
+
if (!a || !b) break;
|
|
55
|
+
if (p >= a[0] && p <= b[0]) {
|
|
56
|
+
if (b[0] === a[0]) return [...b[1]];
|
|
57
|
+
const e = ease((p - a[0]) / (b[0] - a[0]));
|
|
58
|
+
return a[1].map((v, j) => v + ((b[1][j] ?? v) - v) * e);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return [...(stops[stops.length - 1]?.[1] ?? [])];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The rings' slide apart (units of the 1024 box). */
|
|
65
|
+
const SPLIT: readonly Stop[] = [[0, [0]], [0.15, [0]], [0.4, [70]], [0.6, [70]], [0.85, [0]], [1, [0]]];
|
|
66
|
+
/** The big star's dip: [scale, turn in degrees]. */
|
|
67
|
+
const DIP: readonly Stop[] = [[0, [1, 0]], [0.15, [1, 0]], [0.32, [0.7, 45]], [0.5, [1, 90]], [1, [1, 90]]];
|
|
68
|
+
/** The small stars' twinkle: [opacity, scale]. */
|
|
69
|
+
const TWINKLE: readonly Stop[] = [[0, [1, 1]], [0.5, [0.35, 0.6]], [1, [1, 1]]];
|
|
70
|
+
/** The sparkle's small stars trading places (degrees, over two loops). */
|
|
71
|
+
const SWAP: readonly Stop[] = [[0, [0]], [0.15, [0]], [0.5, [180]], [0.65, [180]], [1, [360]]];
|
|
72
|
+
|
|
73
|
+
export interface Pose {
|
|
74
|
+
split: number;
|
|
75
|
+
bigScale: number;
|
|
76
|
+
bigTurn: number;
|
|
77
|
+
twinkleAlpha: number;
|
|
78
|
+
twinkleScale: number;
|
|
79
|
+
swap: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Where every part is `ms` into the animation. */
|
|
83
|
+
export function poseAt(ms: number): Pose {
|
|
84
|
+
const p = (((ms % LOOP_MS) + LOOP_MS) % LOOP_MS) / LOOP_MS;
|
|
85
|
+
const p2 = (((ms % (2 * LOOP_MS)) + 2 * LOOP_MS) % (2 * LOOP_MS)) / (2 * LOOP_MS);
|
|
86
|
+
const [split = 0] = track(SPLIT, p, SMOOTH);
|
|
87
|
+
const [bigScale = 1, bigTurn = 0] = track(DIP, p, SMOOTH);
|
|
88
|
+
const [twinkleAlpha = 1, twinkleScale = 1] = track(TWINKLE, p, EASE_IN_OUT);
|
|
89
|
+
const [swap = 0] = track(SWAP, p2, SMOOTH);
|
|
90
|
+
return { split, bigScale, bigTurn, twinkleAlpha, twinkleScale, swap };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Centres of the stars in the 1024 box (from their bounding boxes). */
|
|
94
|
+
const C_BIG = [512, 512] as const;
|
|
95
|
+
const C_A = [360, 664] as const;
|
|
96
|
+
const C_B = [664, 360] as const;
|
|
97
|
+
|
|
98
|
+
let paths: Record<"ra" | "rb" | "big" | "a" | "b", Path2D> | null = null;
|
|
99
|
+
function shapes(): Record<"ra" | "rb" | "big" | "a" | "b", Path2D> {
|
|
100
|
+
paths ??= { ra: new Path2D(RING_A), rb: new Path2D(RING_B), big: new Path2D(STAR_BIG), a: new Path2D(STAR_A), b: new Path2D(STAR_B) };
|
|
101
|
+
return paths;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface Look {
|
|
105
|
+
variant: "logo" | "sparkle";
|
|
106
|
+
/** [violet, indigo, teal], or three times the text colour for `mono`. */
|
|
107
|
+
colours: readonly [string, string, string];
|
|
108
|
+
/** Flat colours (mono): no ring gradients. */
|
|
109
|
+
flat: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function around(ctx: CanvasRenderingContext2D, [x, y]: readonly [number, number], scale: number, turn: number, path: Path2D, fill: string, alpha: number): void {
|
|
113
|
+
ctx.save();
|
|
114
|
+
ctx.translate(x, y);
|
|
115
|
+
ctx.rotate((turn * Math.PI) / 180);
|
|
116
|
+
ctx.scale(scale, scale);
|
|
117
|
+
ctx.translate(-x, -y);
|
|
118
|
+
ctx.globalAlpha = alpha;
|
|
119
|
+
ctx.fillStyle = fill;
|
|
120
|
+
ctx.fill(path);
|
|
121
|
+
ctx.restore();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Draws one frame on a canvas `BOX` units square (scaled to its pixels). */
|
|
125
|
+
export function drawPose(ctx: CanvasRenderingContext2D, width: number, look: Look, pose: Pose): void {
|
|
126
|
+
const k = width / BOX;
|
|
127
|
+
const [violet, indigo, teal] = look.colours;
|
|
128
|
+
const p = shapes();
|
|
129
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
130
|
+
ctx.clearRect(0, 0, width, width);
|
|
131
|
+
ctx.setTransform(k, 0, 0, k, PAD * k, PAD * k);
|
|
132
|
+
if (look.variant === "logo") {
|
|
133
|
+
const ringA = (() => {
|
|
134
|
+
if (look.flat) return violet;
|
|
135
|
+
const g = ctx.createLinearGradient(312.56, 897.19, 312.56, 2);
|
|
136
|
+
g.addColorStop(0, violet);
|
|
137
|
+
g.addColorStop(1, indigo);
|
|
138
|
+
return g;
|
|
139
|
+
})();
|
|
140
|
+
const ringB = (() => {
|
|
141
|
+
if (look.flat) return violet;
|
|
142
|
+
const g = ctx.createLinearGradient(711.44, 126.81, 711.44, 1022);
|
|
143
|
+
g.addColorStop(0, indigo);
|
|
144
|
+
g.addColorStop(1, teal);
|
|
145
|
+
return g;
|
|
146
|
+
})();
|
|
147
|
+
ctx.save();
|
|
148
|
+
ctx.translate(-pose.split, -pose.split);
|
|
149
|
+
ctx.fillStyle = ringA;
|
|
150
|
+
ctx.fill(p.ra);
|
|
151
|
+
ctx.restore();
|
|
152
|
+
ctx.save();
|
|
153
|
+
ctx.translate(pose.split, pose.split);
|
|
154
|
+
ctx.fillStyle = ringB;
|
|
155
|
+
ctx.fill(p.rb);
|
|
156
|
+
ctx.restore();
|
|
157
|
+
around(ctx, C_BIG, pose.bigScale, pose.bigTurn, p.big, violet, 1);
|
|
158
|
+
around(ctx, C_A, pose.twinkleScale, 0, p.a, indigo, pose.twinkleAlpha);
|
|
159
|
+
around(ctx, C_B, pose.twinkleScale, 0, p.b, teal, pose.twinkleAlpha);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
ctx.translate(512, 512);
|
|
163
|
+
ctx.scale(1.45, 1.45);
|
|
164
|
+
ctx.translate(-512, -512);
|
|
165
|
+
around(ctx, C_BIG, pose.bigScale, pose.bigTurn, p.big, violet, 1);
|
|
166
|
+
ctx.save();
|
|
167
|
+
ctx.translate(512, 512);
|
|
168
|
+
ctx.rotate((pose.swap * Math.PI) / 180);
|
|
169
|
+
ctx.translate(-512, -512);
|
|
170
|
+
ctx.fillStyle = indigo;
|
|
171
|
+
ctx.fill(p.a);
|
|
172
|
+
ctx.fillStyle = teal;
|
|
173
|
+
ctx.fill(p.b);
|
|
174
|
+
ctx.restore();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** A mark the loop draws. */
|
|
178
|
+
export interface Animated {
|
|
179
|
+
canvas: HTMLCanvasElement;
|
|
180
|
+
look: () => Look;
|
|
181
|
+
/** ms of animation shown so far, advanced by the loop. */
|
|
182
|
+
clock: number;
|
|
183
|
+
/** False while off screen: the clock still runs, nothing is drawn. */
|
|
184
|
+
visible: boolean;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const marks = new Set<Animated>();
|
|
188
|
+
let frame = 0;
|
|
189
|
+
let last = 0;
|
|
190
|
+
|
|
191
|
+
/** Speed and pause from the nearest host that sets them. */
|
|
192
|
+
export function motionOf(el: Element): { speed: number; paused: boolean } {
|
|
193
|
+
const host = el.closest("[data-wp-motion-speed], [data-wp-motion-paused]");
|
|
194
|
+
if (!host) return { speed: 1, paused: false };
|
|
195
|
+
const paused = host.closest("[data-wp-motion-paused]") !== null;
|
|
196
|
+
const speedHost = host.closest("[data-wp-motion-speed]");
|
|
197
|
+
const speed = Number(speedHost?.getAttribute("data-wp-motion-speed") ?? "1");
|
|
198
|
+
return { speed: Number.isFinite(speed) && speed >= 0 ? speed : 1, paused };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function fit(c: HTMLCanvasElement): void {
|
|
202
|
+
const css = c.clientWidth;
|
|
203
|
+
const want = Math.max(1, Math.round(css * (window.devicePixelRatio || 1)));
|
|
204
|
+
if (c.width !== want) {
|
|
205
|
+
c.width = want;
|
|
206
|
+
c.height = want;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function tick(now: number): void {
|
|
211
|
+
const dt = last ? Math.min(now - last, 100) : 0;
|
|
212
|
+
last = now;
|
|
213
|
+
for (const m of marks) {
|
|
214
|
+
const { speed, paused } = motionOf(m.canvas);
|
|
215
|
+
if (!paused) m.clock += dt * speed;
|
|
216
|
+
if (!m.visible) continue;
|
|
217
|
+
const ctx = m.canvas.getContext("2d");
|
|
218
|
+
if (!ctx) continue;
|
|
219
|
+
fit(m.canvas);
|
|
220
|
+
drawPose(ctx, m.canvas.width, m.look(), poseAt(m.clock));
|
|
221
|
+
}
|
|
222
|
+
frame = marks.size ? requestAnimationFrame(tick) : 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Starts drawing a mark; returns the function that stops it. */
|
|
226
|
+
export function animate(mark: Animated): () => void {
|
|
227
|
+
marks.add(mark);
|
|
228
|
+
if (!frame) {
|
|
229
|
+
last = 0;
|
|
230
|
+
frame = requestAnimationFrame(tick);
|
|
231
|
+
}
|
|
232
|
+
return () => {
|
|
233
|
+
marks.delete(mark);
|
|
234
|
+
if (!marks.size && frame) {
|
|
235
|
+
cancelAnimationFrame(frame);
|
|
236
|
+
frame = 0;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Draws one still frame right away (the first frame, before the loop runs,
|
|
242
|
+
* so the mark never shows blank). */
|
|
243
|
+
export function drawNow(mark: Animated): boolean {
|
|
244
|
+
const ctx = mark.canvas.getContext("2d");
|
|
245
|
+
if (!ctx) return false;
|
|
246
|
+
fit(mark.canvas);
|
|
247
|
+
drawPose(ctx, mark.canvas.width, mark.look(), poseAt(mark.clock));
|
|
248
|
+
return true;
|
|
249
|
+
}
|
package/src/logo-paths.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
/** The Wildcard Pipeline logo, split into its parts so a drawing can move them
|
|
2
|
-
* on their own (viewBox 0 0 1024 1024). Same shapes as `wp-logo.svg`. */
|
|
3
|
-
export const RING_A =
|
|
4
|
-
"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";
|
|
5
|
-
export const RING_B =
|
|
6
|
-
"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";
|
|
7
|
-
export const STAR_BIG =
|
|
8
|
-
"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";
|
|
9
|
-
export const STAR_A =
|
|
10
|
-
"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";
|
|
11
|
-
export const STAR_B =
|
|
12
|
-
"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";
|
|
13
|
-
|
|
14
|
-
/** The logo's colours: [big star and first ring start, middle, small star and
|
|
15
|
-
* second ring end]. Brand colours, not theme tokens: the mark looks the same
|
|
16
|
-
* in light and dark. `community` is the community site's rust counterpart. */
|
|
17
|
-
export const BRAND_COLOURS = {
|
|
18
|
-
extension: ["#a970ff", "#6366f1", "#33d6c6"],
|
|
19
|
-
community: ["#da7550", "#f1636c", "#ad33d6"],
|
|
20
|
-
} as const;
|
|
21
|
-
export type LogoBrand = keyof typeof BRAND_COLOURS;
|
|
1
|
+
/** The Wildcard Pipeline logo, split into its parts so a drawing can move them
|
|
2
|
+
* on their own (viewBox 0 0 1024 1024). Same shapes as `wp-logo.svg`. */
|
|
3
|
+
export const RING_A =
|
|
4
|
+
"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";
|
|
5
|
+
export const RING_B =
|
|
6
|
+
"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";
|
|
7
|
+
export const STAR_BIG =
|
|
8
|
+
"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";
|
|
9
|
+
export const STAR_A =
|
|
10
|
+
"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";
|
|
11
|
+
export const STAR_B =
|
|
12
|
+
"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";
|
|
13
|
+
|
|
14
|
+
/** The logo's colours: [big star and first ring start, middle, small star and
|
|
15
|
+
* second ring end]. Brand colours, not theme tokens: the mark looks the same
|
|
16
|
+
* in light and dark. `community` is the community site's rust counterpart. */
|
|
17
|
+
export const BRAND_COLOURS = {
|
|
18
|
+
extension: ["#a970ff", "#6366f1", "#33d6c6"],
|
|
19
|
+
community: ["#da7550", "#f1636c", "#ad33d6"],
|
|
20
|
+
} as const;
|
|
21
|
+
export type LogoBrand = keyof typeof BRAND_COLOURS;
|