@sanity/themer 0.8.1 → 0.10.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/README.md +159 -19
- package/dist/AnimatedColorWheelIcon-CwWOVAsw.js +171 -0
- package/dist/AnimatedColorWheelIcon-CwWOVAsw.js.map +1 -0
- package/dist/{ResizableSidebar-DD787kQr.js → ResizableSidebar-C1nr3Ls2.js} +1023 -547
- package/dist/ResizableSidebar-C1nr3Ls2.js.map +1 -0
- package/dist/bundle.css +106 -318
- package/dist/index.d.ts +66 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/themes-au1BLKmY.js +139 -0
- package/dist/themes-au1BLKmY.js.map +1 -0
- package/dist/tool.d.ts +3 -14
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +853 -820
- package/dist/tool.js.map +1 -1
- package/package.json +11 -3
- package/dist/ResizableSidebar-DD787kQr.js.map +0 -1
- package/dist/ResizableSidebar.css.ts.vanilla-M9EuPu-q.js +0 -422
- package/dist/ResizableSidebar.css.ts.vanilla-M9EuPu-q.js.map +0 -1
- package/dist/options-CU-emn_p.d.ts +0 -69
- package/dist/options-CU-emn_p.d.ts.map +0 -1
package/dist/tool.js
CHANGED
|
@@ -1,205 +1,452 @@
|
|
|
1
1
|
import "@sanity/themer/bundle.css";
|
|
2
2
|
import { l as isColor, n as buildTheme, o as SCHEMES, t as presets } from "./presets-CqBgS3A-.js";
|
|
3
|
-
import { a as
|
|
4
|
-
import { blue, cyan, green, magenta, orange, purple, red, yellow } from "@sanity/color";
|
|
3
|
+
import { a as displayTitle, c as resolveThemes, d as viewTransitionClasses, f as NAVBAR_SELECTOR, i as createCustomTheme, l as addThemerTransitionType, n as CONFIG_TITLE, o as duplicateTitle, s as initialThemerState, u as layout } from "./themes-au1BLKmY.js";
|
|
5
4
|
import { c } from "react/compiler-runtime";
|
|
6
5
|
import { definePlugin, useColorSchemeValue } from "sanity";
|
|
7
|
-
import { Button, Card, Flex, Text, ThemeProvider, useMediaIndex, usePrefersReducedMotion } from "@sanity/ui";
|
|
8
|
-
import { useActor, useSelector } from "@xstate/react";
|
|
9
|
-
import { Activity, ViewTransition, addTransitionType, lazy, startTransition, useDeferredValue, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
10
|
-
import { assign, not, or, setup, stateIn } from "xstate";
|
|
11
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
6
|
import { ColorWheelIcon } from "@sanity/icons/ColorWheel";
|
|
7
|
+
import { BoundaryElementProvider, Box, Button, Card, Flex, Text, ThemeProvider, useMediaIndex, usePrefersReducedMotion } from "@sanity/ui";
|
|
13
8
|
import { Tooltip } from "@sanity/ui/tooltip";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
import { Activity, Suspense, ViewTransition, createContext, lazy, startTransition, use, useEffect, useReducer, useRef, useState, useTransition } from "react";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { browser } from "react-dom";
|
|
12
|
+
import { dequal } from "dequal/lite";
|
|
13
|
+
import * as v from "valibot";
|
|
14
|
+
import { useActorRef } from "@xstate/react";
|
|
15
|
+
import { assign, not, setup } from "xstate";
|
|
16
|
+
/** @internal */
|
|
17
|
+
const ToolIsOpenContext = createContext(!1), ToolDispatchContext = createContext(() => {
|
|
18
|
+
throw Error("ToolDispatchContext not initialized");
|
|
19
|
+
}), ToolSplitIsOpenContext = createContext(!1), ToolShouldDetectNavbarHeightContext = createContext(!1), PluginConfigContext = createContext(null), AnimatedColorWheelIcon = lazy(() => import("./AnimatedColorWheelIcon-CwWOVAsw.js"));
|
|
20
|
+
function ThemerNavbarButton() {
|
|
21
|
+
let $ = c(27), config = use(PluginConfigContext), dispatch = use(ToolDispatchContext), open = use(ToolIsOpenContext), shouldDetectNavbarHeight = use(ToolShouldDetectNavbarHeightContext), split = use(ToolSplitIsOpenContext);
|
|
22
|
+
if (!config) throw TypeError("PluginConfigContext.Provider is missing");
|
|
23
|
+
let [busy, startBusyTransition] = useTransition(), iconRef = useRef(null), prefersReducedMotion = usePrefersReducedMotion(), t0;
|
|
24
|
+
$[0] !== dispatch || $[1] !== split ? (t0 = () => startBusyTransition(() => {
|
|
25
|
+
split && addThemerTransitionType("split-screen:open"), addThemerTransitionType("open"), dispatch({ type: "open" });
|
|
26
|
+
}), $[0] = dispatch, $[1] = split, $[2] = t0) : t0 = $[2];
|
|
27
|
+
let handleOpen = t0, t1;
|
|
28
|
+
$[3] !== dispatch || $[4] !== split ? (t1 = () => startBusyTransition(() => {
|
|
29
|
+
split && addThemerTransitionType("split-screen:close"), addThemerTransitionType("close"), dispatch({ type: "close" });
|
|
30
|
+
}), $[3] = dispatch, $[4] = split, $[5] = t1) : t1 = $[5];
|
|
31
|
+
let handleClose = t1, t2;
|
|
32
|
+
$[6] !== dispatch || $[7] !== open ? (t2 = () => startTransition(() => {
|
|
33
|
+
dispatch({ type: "prerender" }), open || iconRef.current?.spin();
|
|
34
|
+
}), $[6] = dispatch, $[7] = open, $[8] = t2) : t2 = $[8];
|
|
35
|
+
let handlePrerender = t2, t3;
|
|
36
|
+
$[9] === dispatch ? t3 = $[10] : (t3 = (root) => {
|
|
37
|
+
let navbar = root?.closest(NAVBAR_SELECTOR);
|
|
38
|
+
if (!navbar) return;
|
|
39
|
+
let resizeObserver = new ResizeObserver((entries) => {
|
|
40
|
+
let height = entries[0]?.borderBoxSize[0]?.blockSize;
|
|
41
|
+
height && startTransition(() => dispatch({
|
|
42
|
+
type: "set-navbar-height",
|
|
43
|
+
height
|
|
44
|
+
}));
|
|
45
|
+
});
|
|
46
|
+
return resizeObserver.observe(navbar), () => resizeObserver.disconnect();
|
|
47
|
+
}, $[9] = dispatch, $[10] = t3);
|
|
48
|
+
let observeNavbarHeight = t3, { title } = config, t4;
|
|
49
|
+
$[11] === title ? t4 = $[12] : (t4 = /* @__PURE__ */ jsx(Text, {
|
|
50
|
+
size: 1,
|
|
51
|
+
children: title
|
|
52
|
+
}), $[11] = title, $[12] = t4);
|
|
53
|
+
let t5 = shouldDetectNavbarHeight ? observeNavbarHeight : void 0, t6;
|
|
54
|
+
$[13] !== busy || $[14] !== prefersReducedMotion ? (t6 = prefersReducedMotion ? /* @__PURE__ */ jsx(ColorWheelIcon, {}) : /* @__PURE__ */ jsx(Suspense, {
|
|
55
|
+
fallback: /* @__PURE__ */ jsx(ColorWheelIcon, {}),
|
|
56
|
+
children: /* @__PURE__ */ jsx(AnimatedColorWheelIcon, {
|
|
57
|
+
busy,
|
|
58
|
+
ref: iconRef
|
|
59
|
+
})
|
|
60
|
+
}), $[13] = busy, $[14] = prefersReducedMotion, $[15] = t6) : t6 = $[15];
|
|
61
|
+
let t7 = open ? handleClose : handleOpen, t8 = open || busy, t9;
|
|
62
|
+
$[16] !== busy || $[17] !== handlePrerender || $[18] !== t5 || $[19] !== t6 || $[20] !== t7 || $[21] !== t8 || $[22] !== title ? (t9 = /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(ViewTransition, {
|
|
63
|
+
default: "none",
|
|
64
|
+
update: viewTransitionClasses.navbarButton.update,
|
|
65
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
66
|
+
ref: t5,
|
|
67
|
+
"aria-busy": busy,
|
|
68
|
+
"aria-label": title,
|
|
69
|
+
icon: t6,
|
|
70
|
+
mode: "bleed",
|
|
71
|
+
onClick: t7,
|
|
72
|
+
onFocus: handlePrerender,
|
|
73
|
+
onMouseEnter: handlePrerender,
|
|
74
|
+
padding: 2,
|
|
75
|
+
selected: t8
|
|
76
|
+
})
|
|
77
|
+
}) }), $[16] = busy, $[17] = handlePrerender, $[18] = t5, $[19] = t6, $[20] = t7, $[21] = t8, $[22] = title, $[23] = t9) : t9 = $[23];
|
|
78
|
+
let t10;
|
|
79
|
+
return $[24] !== t4 || $[25] !== t9 ? (t10 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
80
|
+
default: "none",
|
|
81
|
+
update: viewTransitionClasses.navbarButtonTooltip.update,
|
|
82
|
+
children: /* @__PURE__ */ jsx(Tooltip, {
|
|
83
|
+
animate: !0,
|
|
84
|
+
content: t4,
|
|
85
|
+
portal: !0,
|
|
86
|
+
children: t9
|
|
87
|
+
})
|
|
88
|
+
}), $[24] = t4, $[25] = t9, $[26] = t10) : t10 = $[26], t10;
|
|
18
89
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
90
|
+
function ThemerNavbar(props) {
|
|
91
|
+
"use memo";
|
|
92
|
+
let $ = c(4), t0;
|
|
93
|
+
if ($[0] !== props) {
|
|
94
|
+
let button = {
|
|
95
|
+
location: "topbar",
|
|
96
|
+
name: "themer-topbar",
|
|
97
|
+
render: ThemerNavbarButton
|
|
98
|
+
}, t1;
|
|
99
|
+
$[2] === props.__internal_actions ? t1 = $[3] : (t1 = props.__internal_actions ?? [], $[2] = props.__internal_actions, $[3] = t1), t0 = props.renderDefault({
|
|
100
|
+
...props,
|
|
101
|
+
__internal_actions: [...t1, button]
|
|
102
|
+
}), $[0] = props, $[1] = t0;
|
|
103
|
+
} else t0 = $[1];
|
|
104
|
+
return t0;
|
|
21
105
|
}
|
|
22
|
-
function
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
106
|
+
function ThemerNavbarProvider(t0) {
|
|
107
|
+
let $ = c(15), { children, config, dispatch, open, shouldDetectNavbarHeight, split } = t0, t1 = shouldDetectNavbarHeight ?? !1, t2;
|
|
108
|
+
$[0] !== children || $[1] !== t1 ? (t2 = /* @__PURE__ */ jsx(ToolShouldDetectNavbarHeightContext, {
|
|
109
|
+
value: t1,
|
|
110
|
+
children
|
|
111
|
+
}), $[0] = children, $[1] = t1, $[2] = t2) : t2 = $[2];
|
|
112
|
+
let t3;
|
|
113
|
+
$[3] !== split || $[4] !== t2 ? (t3 = /* @__PURE__ */ jsx(ToolSplitIsOpenContext, {
|
|
114
|
+
value: split,
|
|
115
|
+
children: t2
|
|
116
|
+
}), $[3] = split, $[4] = t2, $[5] = t3) : t3 = $[5];
|
|
117
|
+
let t4;
|
|
118
|
+
$[6] !== open || $[7] !== t3 ? (t4 = /* @__PURE__ */ jsx(ToolIsOpenContext, {
|
|
119
|
+
value: open,
|
|
120
|
+
children: t3
|
|
121
|
+
}), $[6] = open, $[7] = t3, $[8] = t4) : t4 = $[8];
|
|
122
|
+
let t5;
|
|
123
|
+
$[9] !== dispatch || $[10] !== t4 ? (t5 = /* @__PURE__ */ jsx(ToolDispatchContext, {
|
|
124
|
+
value: dispatch,
|
|
125
|
+
children: t4
|
|
126
|
+
}), $[9] = dispatch, $[10] = t4, $[11] = t5) : t5 = $[11];
|
|
127
|
+
let t6;
|
|
128
|
+
return $[12] !== config || $[13] !== t5 ? (t6 = /* @__PURE__ */ jsx(PluginConfigContext, {
|
|
129
|
+
value: config,
|
|
130
|
+
children: t5
|
|
131
|
+
}), $[12] = config, $[13] = t5, $[14] = t6) : t6 = $[14], t6;
|
|
132
|
+
}
|
|
133
|
+
function toolReducer(state, action) {
|
|
134
|
+
switch (action.type) {
|
|
135
|
+
case "set-navbar-height": return state.navbarHeight === action.height ? state : {
|
|
136
|
+
...state,
|
|
137
|
+
navbarHeight: action.height
|
|
138
|
+
};
|
|
139
|
+
case "prerender": return state.prerender ? state : {
|
|
140
|
+
...state,
|
|
141
|
+
prerender: !0
|
|
142
|
+
};
|
|
143
|
+
case "open": return state.open ? state : {
|
|
144
|
+
...state,
|
|
145
|
+
open: !0,
|
|
146
|
+
prerender: !0
|
|
147
|
+
};
|
|
148
|
+
case "close": return state.open ? {
|
|
149
|
+
...state,
|
|
150
|
+
open: !1
|
|
151
|
+
} : state;
|
|
152
|
+
case "split-screen:prerender": return state.prerenderSplitScreen ? state : {
|
|
153
|
+
...state,
|
|
154
|
+
prerenderSplitScreen: !0
|
|
155
|
+
};
|
|
156
|
+
case "split-screen:open": return state.split ? state : {
|
|
157
|
+
...state,
|
|
158
|
+
prerenderSplitScreen: !0,
|
|
159
|
+
split: !0
|
|
160
|
+
};
|
|
161
|
+
case "split-screen:close": return state.split ? {
|
|
162
|
+
...state,
|
|
163
|
+
split: !1
|
|
164
|
+
} : state;
|
|
165
|
+
case "unset-theme": return state.theme === null ? state : {
|
|
166
|
+
...state,
|
|
167
|
+
theme: null
|
|
168
|
+
};
|
|
169
|
+
case "set-theme": return dequal(state.theme, action.theme) ? state : {
|
|
170
|
+
...state,
|
|
171
|
+
theme: action.theme
|
|
172
|
+
};
|
|
173
|
+
default: throw Error(`Unknown themer tool action: ${JSON.stringify(action)}`);
|
|
174
|
+
}
|
|
27
175
|
}
|
|
28
176
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
177
|
+
* Where a tab starts: closed, nothing prerendered, no theme applied —
|
|
178
|
+
* `ThemerProvider` seeds only the applied theme, from the machine's restored
|
|
179
|
+
* snapshot
|
|
32
180
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
181
|
+
const initialToolState = {
|
|
182
|
+
navbarHeight: null,
|
|
183
|
+
open: !1,
|
|
184
|
+
prerender: !1,
|
|
185
|
+
prerenderSplitScreen: !1,
|
|
186
|
+
split: !1,
|
|
187
|
+
theme: null
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* The options of the applied theme — `null` for the configured theme, which
|
|
191
|
+
* applies nothing on top of the Studio's own, and for an applied theme that
|
|
192
|
+
* no longer resolves. Cheaper than resolving the whole list when only the
|
|
193
|
+
* applied theme matters.
|
|
194
|
+
*
|
|
195
|
+
* @internal
|
|
196
|
+
*/
|
|
197
|
+
function resolveActiveThemeOptions(state) {
|
|
198
|
+
if (state.active === null || state.removed.includes(state.active)) return null;
|
|
199
|
+
for (let theme of state.custom) if (theme.slug === state.active) return theme.options;
|
|
200
|
+
for (let preset of presets) if (preset.slug === state.active) return preset.options;
|
|
201
|
+
return null;
|
|
47
202
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
203
|
+
/** Drops the keys whose value is `undefined`, which a dropped option leaves behind */
|
|
204
|
+
function compact(value) {
|
|
205
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0));
|
|
206
|
+
}
|
|
207
|
+
/** A `#rgb` or `#rrggbb` color, lowercased */
|
|
208
|
+
const color = v.pipe(v.string(), v.check(isColor), v.toLowerCase()), optionalColor = v.fallback(v.optional(color), void 0), optionalContrast = v.fallback(v.optional(v.pipe(v.number(), v.finite(), v.toMinValue(15), v.toMaxValue(100))), void 0), schemeOptionsSchema = v.pipe(v.object({
|
|
209
|
+
accent: optionalColor,
|
|
210
|
+
text: optionalColor,
|
|
211
|
+
background: optionalColor,
|
|
212
|
+
contrast: optionalContrast
|
|
213
|
+
}), v.transform((options) => compact(options))), groupedOptionsSchema = v.pipe(v.looseObject({
|
|
214
|
+
light: v.fallback(v.optional(schemeOptionsSchema), void 0),
|
|
215
|
+
dark: v.fallback(v.optional(schemeOptionsSchema), void 0)
|
|
216
|
+
}), v.check((input) => !("accent" in input), "flat options"), v.transform(({ light, dark }) => {
|
|
51
217
|
let options = {};
|
|
218
|
+
return light && Object.keys(light).length > 0 && (options.light = light), dark && Object.keys(dark).length > 0 && (options.dark = dark), options;
|
|
219
|
+
})), flatOptionsSchema = v.pipe(v.object({
|
|
220
|
+
accent: color,
|
|
221
|
+
text: optionalColor,
|
|
222
|
+
contrast: optionalContrast,
|
|
223
|
+
background: v.fallback(v.optional(v.object({
|
|
224
|
+
light: optionalColor,
|
|
225
|
+
dark: optionalColor
|
|
226
|
+
})), void 0)
|
|
227
|
+
}), v.transform(({ accent, text, contrast, background }) => {
|
|
228
|
+
let shared = compact({
|
|
229
|
+
accent,
|
|
230
|
+
text,
|
|
231
|
+
contrast
|
|
232
|
+
}), options = {};
|
|
52
233
|
for (let scheme of SCHEMES) {
|
|
53
|
-
let
|
|
54
|
-
|
|
234
|
+
let schemeBackground = background?.[scheme];
|
|
235
|
+
options[scheme] = schemeBackground ? {
|
|
236
|
+
...shared,
|
|
237
|
+
background: schemeBackground
|
|
238
|
+
} : { ...shared };
|
|
55
239
|
}
|
|
56
240
|
return options;
|
|
57
|
-
}
|
|
241
|
+
})), themeOptionsSchema = v.union([flatOptionsSchema, groupedOptionsSchema]), swatch = v.fallback(v.optional(v.nullable(color), null), null), paletteSchema = v.pipe(v.object({
|
|
242
|
+
vibrant: swatch,
|
|
243
|
+
lightVibrant: swatch,
|
|
244
|
+
darkVibrant: swatch,
|
|
245
|
+
muted: swatch,
|
|
246
|
+
lightMuted: swatch,
|
|
247
|
+
darkMuted: swatch,
|
|
248
|
+
dominant: swatch
|
|
249
|
+
}), v.transform((palette) => Object.values(palette).some((item) => item !== null) ? palette : null));
|
|
58
250
|
function isReservedSlug(slug) {
|
|
59
251
|
return slug === "config" || presets.some((preset) => preset.slug === slug);
|
|
60
252
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
let
|
|
78
|
-
for (let
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
return swatches > 0 ? palette : null;
|
|
83
|
-
}
|
|
84
|
-
function sanitizeState(value) {
|
|
85
|
-
if (!value || typeof value != "object") return initialThemerState;
|
|
86
|
-
let custom = [], rawCustom = Reflect.get(value, "custom");
|
|
87
|
-
if (Array.isArray(rawCustom)) for (let item of rawCustom) {
|
|
88
|
-
let theme = sanitizeCustomTheme(item);
|
|
89
|
-
theme && !custom.some((existing) => existing.slug === theme.slug) && custom.push(theme);
|
|
253
|
+
const customThemeSchema = v.pipe(v.object({
|
|
254
|
+
slug: v.pipe(v.string(), v.nonEmpty(), v.check((slug) => !isReservedSlug(slug), "reserved slug")),
|
|
255
|
+
title: v.fallback(v.optional(v.string()), void 0),
|
|
256
|
+
options: themeOptionsSchema,
|
|
257
|
+
palette: v.fallback(v.optional(paletteSchema), void 0)
|
|
258
|
+
}), v.transform(({ slug, title, options, palette }) => ({
|
|
259
|
+
slug,
|
|
260
|
+
title: displayTitle(title ?? ""),
|
|
261
|
+
options,
|
|
262
|
+
...palette ? { palette } : {}
|
|
263
|
+
}))), lenientList = v.fallback(v.optional(v.array(v.unknown()), []), []), themerStateSchema = v.pipe(v.object({
|
|
264
|
+
active: v.fallback(v.nullable(v.string()), null),
|
|
265
|
+
custom: lenientList,
|
|
266
|
+
removed: lenientList,
|
|
267
|
+
order: lenientList
|
|
268
|
+
}), v.transform((state) => {
|
|
269
|
+
let custom = [];
|
|
270
|
+
for (let item of state.custom) {
|
|
271
|
+
let result = v.safeParse(customThemeSchema, item);
|
|
272
|
+
result.success && !custom.some((theme) => theme.slug === result.output.slug) && custom.push(result.output);
|
|
90
273
|
}
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
let order
|
|
94
|
-
if (Array.isArray(rawOrder)) for (let slug of rawOrder) typeof slug == "string" && (slug === "config" || presets.some((preset) => preset.slug === slug) || custom.some((theme) => theme.slug === slug)) && !order.includes(slug) && order.push(slug);
|
|
95
|
-
let rawActive = Reflect.get(value, "active");
|
|
274
|
+
let isCustom = (slug) => custom.some((theme) => theme.slug === slug), isPreset = (slug) => presets.some((preset) => preset.slug === slug), removed = [], order = [];
|
|
275
|
+
for (let slug of state.removed) typeof slug != "string" || slug === "config" || removed.includes(slug) || (isPreset(slug) || isCustom(slug)) && removed.push(slug);
|
|
276
|
+
for (let slug of state.order) typeof slug != "string" || order.includes(slug) || (slug === "config" || isPreset(slug) || isCustom(slug)) && order.push(slug);
|
|
96
277
|
return {
|
|
97
|
-
active:
|
|
278
|
+
active: state.active,
|
|
98
279
|
custom,
|
|
99
280
|
removed,
|
|
100
281
|
order
|
|
101
282
|
};
|
|
283
|
+
}));
|
|
284
|
+
/** The persisted themer state, or `null` for anything else @internal */
|
|
285
|
+
function parseThemerState(input) {
|
|
286
|
+
let result = v.safeParse(themerStateSchema, input);
|
|
287
|
+
return result.success ? result.output : null;
|
|
102
288
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
289
|
+
/** The flows a session restores to */
|
|
290
|
+
const flowSchema = v.picklist([
|
|
291
|
+
"list",
|
|
292
|
+
"edit",
|
|
293
|
+
"removed"
|
|
294
|
+
]), snapshotValueSchema = v.object({
|
|
295
|
+
flow: v.union([flowSchema, v.object({ list: v.picklist([
|
|
296
|
+
"idle",
|
|
297
|
+
"pasting",
|
|
298
|
+
"snippet"
|
|
299
|
+
]) })]),
|
|
300
|
+
theme: v.picklist(["applied", "switching"])
|
|
301
|
+
});
|
|
302
|
+
/**
|
|
303
|
+
* A snapshot for the machine to start a session from: the given themes, with
|
|
304
|
+
* nothing being switched to, in the list unless a flow and its subject are
|
|
305
|
+
* given. It is how the themes alone — all that earlier versions persisted —
|
|
306
|
+
* are handed to a machine that only restores snapshots, and what a restored
|
|
307
|
+
* snapshot is made fit for this session as.
|
|
308
|
+
*
|
|
309
|
+
* @internal
|
|
310
|
+
*/
|
|
311
|
+
function snapshotFromState(state, editing = null, flow = "list") {
|
|
109
312
|
return {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
313
|
+
status: "active",
|
|
314
|
+
output: void 0,
|
|
315
|
+
error: void 0,
|
|
316
|
+
value: {
|
|
317
|
+
flow,
|
|
318
|
+
theme: "applied"
|
|
319
|
+
},
|
|
320
|
+
context: {
|
|
321
|
+
...state,
|
|
322
|
+
editing,
|
|
323
|
+
images: {}
|
|
324
|
+
},
|
|
325
|
+
historyValue: {},
|
|
326
|
+
children: {}
|
|
114
327
|
};
|
|
115
328
|
}
|
|
116
329
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
330
|
+
* The machine's snapshot as the last session persisted it, made fit for this
|
|
331
|
+
* one: the themes are parsed like any persisted state, the flow keeps its
|
|
332
|
+
* subject or falls back to the list, a dialog that was open is closed, a
|
|
333
|
+
* switch that was under way is over, and the image URLs of the last session
|
|
334
|
+
* are gone with it.
|
|
120
335
|
*
|
|
121
336
|
* @internal
|
|
122
337
|
*/
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
338
|
+
const persistedSnapshotSchema = v.pipe(v.object({
|
|
339
|
+
status: v.literal("active"),
|
|
340
|
+
value: snapshotValueSchema,
|
|
341
|
+
context: v.pipe(v.looseObject({ editing: v.fallback(v.nullable(v.object({
|
|
342
|
+
slug: v.string(),
|
|
343
|
+
focusTitle: v.boolean()
|
|
344
|
+
})), null) }), v.transform(({ editing, ...state }) => ({
|
|
345
|
+
editing,
|
|
346
|
+
state: parseThemerState(state)
|
|
347
|
+
})), v.check(({ state }) => state !== null, "no themes"))
|
|
348
|
+
}), v.transform(({ value, context }) => {
|
|
349
|
+
let state = context.state ?? {
|
|
350
|
+
active: null,
|
|
351
|
+
custom: [],
|
|
352
|
+
removed: [],
|
|
353
|
+
order: []
|
|
354
|
+
}, editing = context.editing && state.custom.some((theme) => theme.slug === context.editing?.slug) && !state.removed.includes(context.editing.slug) ? context.editing : null;
|
|
355
|
+
return snapshotFromState(state, editing, typeof value.flow != "string" || value.flow === "edit" && editing === null || value.flow === "removed" && state.removed.length === 0 ? "list" : value.flow);
|
|
356
|
+
})), SNAPSHOT_STORAGE_KEY = "sanityStudio:themer:snapshot", STATE_STORAGE_KEY = "sanityStudio:themer:state", LEGACY_STORAGE_KEY = "sanityStudio:themer:options", LEGACY_DRAFT = {
|
|
357
|
+
slug: "custom-draft",
|
|
358
|
+
title: "Draft theme"
|
|
359
|
+
};
|
|
360
|
+
function readJson(key) {
|
|
361
|
+
let raw = localStorage.getItem(key);
|
|
362
|
+
return raw ? JSON.parse(raw) : void 0;
|
|
138
363
|
}
|
|
139
364
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
365
|
+
* Restores the machine's snapshot from what the last session persisted, for
|
|
366
|
+
* `createActor` to start from, so the user's themes survive Studio reloads:
|
|
367
|
+
* the snapshot itself where there is one that fits, and one made from the
|
|
368
|
+
* themes alone where the snapshot no longer fits the machine or where
|
|
369
|
+
* earlier versions of the tool stored the themes without a snapshot.
|
|
370
|
+
* `undefined` when nothing usable is stored — or storage cannot be read at
|
|
371
|
+
* all — leaves the machine to start from no themes.
|
|
372
|
+
*
|
|
373
|
+
* The parsing is the schemas' (see `schemas.ts`): nothing here or in the
|
|
374
|
+
* machine looks at what is stored beyond handing it over.
|
|
142
375
|
*
|
|
143
376
|
* @internal
|
|
144
377
|
*/
|
|
145
|
-
function
|
|
378
|
+
function readPersistedSnapshot() {
|
|
146
379
|
try {
|
|
147
|
-
|
|
380
|
+
if (typeof localStorage > "u") return;
|
|
381
|
+
let persisted = readJson(SNAPSHOT_STORAGE_KEY);
|
|
382
|
+
if (persisted !== void 0) {
|
|
383
|
+
let snapshot = v.safeParse(persistedSnapshotSchema, persisted);
|
|
384
|
+
if (snapshot.success) return snapshot.output;
|
|
385
|
+
let state = parseThemerState(persisted && typeof persisted == "object" ? Reflect.get(persisted, "context") : void 0);
|
|
386
|
+
if (state) return snapshotFromState(state);
|
|
387
|
+
}
|
|
388
|
+
let state = parseThemerState(readJson(STATE_STORAGE_KEY));
|
|
389
|
+
return state ? snapshotFromState(state) : readLegacyDraft();
|
|
148
390
|
} catch {
|
|
149
|
-
return
|
|
391
|
+
return;
|
|
150
392
|
}
|
|
151
393
|
}
|
|
394
|
+
function readLegacyDraft() {
|
|
395
|
+
let options = v.safeParse(themeOptionsSchema, readJson(LEGACY_STORAGE_KEY));
|
|
396
|
+
if (!options.success) return;
|
|
397
|
+
let theme = {
|
|
398
|
+
...LEGACY_DRAFT,
|
|
399
|
+
options: options.output
|
|
400
|
+
};
|
|
401
|
+
return snapshotFromState({
|
|
402
|
+
active: theme.slug,
|
|
403
|
+
custom: [theme],
|
|
404
|
+
removed: [],
|
|
405
|
+
order: []
|
|
406
|
+
});
|
|
407
|
+
}
|
|
152
408
|
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
409
|
+
* Persists the machine's snapshot for the next session, and lets go of what
|
|
410
|
+
* earlier versions stored once it is written — which completes their
|
|
411
|
+
* migration.
|
|
155
412
|
*
|
|
156
413
|
* @internal
|
|
157
414
|
*/
|
|
158
|
-
function
|
|
415
|
+
function writePersistedSnapshot(snapshot) {
|
|
159
416
|
try {
|
|
160
|
-
if (typeof localStorage > "u"
|
|
161
|
-
localStorage.setItem(
|
|
417
|
+
if (typeof localStorage > "u") return;
|
|
418
|
+
localStorage.setItem(SNAPSHOT_STORAGE_KEY, JSON.stringify(snapshot)), localStorage.removeItem(STATE_STORAGE_KEY), localStorage.removeItem(LEGACY_STORAGE_KEY);
|
|
162
419
|
} catch {}
|
|
163
420
|
}
|
|
164
|
-
/**
|
|
165
|
-
* How long the layout's motions — the panel's and the split preview's, and
|
|
166
|
-
* the cross-fade to another theme — are taken to last when the layout never
|
|
167
|
-
* says: it reports every view transition it starts, so this only ends a
|
|
168
|
-
* motion that has none (reduced motion, a browser without view transitions),
|
|
169
|
-
* where the `moving` and `switching` tags have nothing to affect. Generous,
|
|
170
|
-
* so that it never runs out before a slow deferred commit starts the real one.
|
|
171
|
-
*
|
|
172
|
-
* @internal
|
|
173
|
-
*/
|
|
174
|
-
const MOTION_DURATION = 2e3;
|
|
175
421
|
function themesOf(context) {
|
|
176
|
-
return resolveThemes(context
|
|
422
|
+
return resolveThemes(context);
|
|
177
423
|
}
|
|
178
424
|
function revokeObjectUrl(url) {
|
|
179
425
|
typeof URL < "u" && typeof URL.revokeObjectURL == "function" && URL.revokeObjectURL(url);
|
|
180
426
|
}
|
|
181
427
|
/**
|
|
182
|
-
* The
|
|
183
|
-
* `
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* applied theme, the user's themes
|
|
188
|
-
* flows need.
|
|
428
|
+
* The themes of the themer tool and the sidebar's way through them: which
|
|
429
|
+
* `flow` the sidebar is in — picking a theme from the `list`, with its
|
|
430
|
+
* dialogs for pasting a shared code and for the applied theme's snippet,
|
|
431
|
+
* `edit`ing one of the user's own themes, or restoring `removed` ones — and,
|
|
432
|
+
* in a parallel `theme` region, whether another theme was just applied. The
|
|
433
|
+
* context carries the persisted state (the applied theme, the user's themes
|
|
434
|
+
* and what was removed) alongside what the flows need. Whether the sidebar is
|
|
435
|
+
* open and whether the Studio shows twice is not the machine's: the tool
|
|
436
|
+
* reducer in `ThemerProvider` owns that, so that the navbar toggle and the
|
|
437
|
+
* sidebar can change it in transitions of their own. Nor is persisting: the
|
|
438
|
+
* machine never touches storage and takes no input — it starts from no
|
|
439
|
+
* themes, and a session with something to pick up from restores its
|
|
440
|
+
* persisted snapshot instead (see `storage.ts` and `sync.ts`), while the
|
|
441
|
+
* persisted state of other tabs reaches it as a `themes.sync` event like any
|
|
442
|
+
* other.
|
|
189
443
|
*
|
|
190
|
-
* The
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* that applies another theme — picking one, adding, duplicating, importing or
|
|
197
|
-
* editing one, removing or deleting the applied one — when the layout
|
|
198
|
-
* cross-fades to it; edits to the applied theme's colors are not switches,
|
|
199
|
-
* they follow the pointer. These states last until the layout reports its
|
|
200
|
-
* transition under way — every boundary reports, the panel's and the copy's
|
|
201
|
-
* entering and leaving as much as the Studio's morphing — or for
|
|
202
|
-
* `MOTION_DURATION` when nothing animates.
|
|
444
|
+
* The `theme` region is `switching` (its tag) right after an event that
|
|
445
|
+
* applies another theme — picking one, adding, duplicating, importing or
|
|
446
|
+
* editing one, removing or deleting the applied one — which is when the
|
|
447
|
+
* layout cross-fades the Studio to it. Edits to the applied theme's colors are
|
|
448
|
+
* not switches, they follow the pointer: the first one ends the switch, and so
|
|
449
|
+
* does `MOTION_DURATION` when none follows.
|
|
203
450
|
*
|
|
204
451
|
* Theme operations are handled in every flow, and the flows leave on their
|
|
205
452
|
* own when they lose their subject: the editor when its theme is removed or
|
|
@@ -210,34 +457,31 @@ function revokeObjectUrl(url) {
|
|
|
210
457
|
const themerMachine = setup({
|
|
211
458
|
types: {
|
|
212
459
|
context: {},
|
|
213
|
-
events: {}
|
|
214
|
-
input: {}
|
|
460
|
+
events: {}
|
|
215
461
|
},
|
|
216
462
|
guards: {
|
|
217
463
|
isCustomTheme: ({ context }, params) => themesOf(context).themes.some((theme) => theme.slug === params.slug && theme.source === "custom"),
|
|
218
464
|
isEditingListedTheme: ({ context }) => context.editing !== null && themesOf(context).themes.some((theme) => theme.slug === context.editing?.slug && theme.source === "custom"),
|
|
219
465
|
hasRemovedThemes: ({ context }) => themesOf(context).removed.length > 0,
|
|
220
|
-
|
|
466
|
+
hasAppliedTheme: ({ context }) => themesOf(context).active !== void 0,
|
|
221
467
|
isAnotherTheme: ({ context }, params) => params.slug !== (context.active ?? "config"),
|
|
222
468
|
isAppliedTheme: ({ context }, params) => params.slug === context.active,
|
|
469
|
+
appliesAnotherTheme: ({ context }, params) => params.state.active !== context.active,
|
|
223
470
|
hasOptions: (_, params) => params.options !== void 0
|
|
224
471
|
},
|
|
225
472
|
actions: {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
order
|
|
233
|
-
});
|
|
234
|
-
},
|
|
473
|
+
sync: assign((_, params) => ({
|
|
474
|
+
active: params.state.active,
|
|
475
|
+
custom: params.state.custom,
|
|
476
|
+
removed: params.state.removed,
|
|
477
|
+
order: params.state.order
|
|
478
|
+
})),
|
|
235
479
|
revokeImage: (_, params) => {
|
|
236
480
|
params.url && revokeObjectUrl(params.url);
|
|
237
481
|
},
|
|
238
482
|
pick: assign((_, params) => ({ active: params.slug === "config" ? null : params.slug })),
|
|
239
483
|
add: assign(({ context }, params) => {
|
|
240
|
-
let theme = createCustomTheme(params.title ?? "Untitled theme", params.options ?? themesOf(context).active
|
|
484
|
+
let theme = createCustomTheme(params.title ?? "Untitled theme", params.options ?? themesOf(context).active?.options ?? {}, params.palette);
|
|
241
485
|
return {
|
|
242
486
|
active: theme.slug,
|
|
243
487
|
custom: [...context.custom, theme],
|
|
@@ -259,7 +503,11 @@ const themerMachine = setup({
|
|
|
259
503
|
};
|
|
260
504
|
}),
|
|
261
505
|
duplicate: assign(({ context }, params) => {
|
|
262
|
-
let { themes, removed } = themesOf(context), source =
|
|
506
|
+
let { themes, removed } = themesOf(context), source = params.slug === "config" ? {
|
|
507
|
+
title: CONFIG_TITLE,
|
|
508
|
+
options: {},
|
|
509
|
+
palette: void 0
|
|
510
|
+
} : [...themes, ...removed].find((theme) => theme.slug === params.slug);
|
|
263
511
|
if (!source) return {};
|
|
264
512
|
let theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette);
|
|
265
513
|
return {
|
|
@@ -313,83 +561,13 @@ const themerMachine = setup({
|
|
|
313
561
|
}
|
|
314
562
|
}).createMachine({
|
|
315
563
|
id: "themer",
|
|
316
|
-
context: (
|
|
317
|
-
...
|
|
318
|
-
baseOptions: input.baseOptions,
|
|
564
|
+
context: () => ({
|
|
565
|
+
...initialThemerState,
|
|
319
566
|
editing: null,
|
|
320
567
|
images: {}
|
|
321
568
|
}),
|
|
322
|
-
entry: "persist",
|
|
323
569
|
type: "parallel",
|
|
324
570
|
states: {
|
|
325
|
-
sidebar: {
|
|
326
|
-
initial: "closed",
|
|
327
|
-
states: {
|
|
328
|
-
closed: { on: { "sidebar.toggle": "opening" } },
|
|
329
|
-
opening: {
|
|
330
|
-
tags: ["panel", "moving"],
|
|
331
|
-
after: { [MOTION_DURATION]: "open" },
|
|
332
|
-
on: {
|
|
333
|
-
"layout.transitioned": "open",
|
|
334
|
-
"sidebar.toggle": "closing",
|
|
335
|
-
"sidebar.close": "closing"
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
open: {
|
|
339
|
-
tags: ["panel"],
|
|
340
|
-
on: {
|
|
341
|
-
"sidebar.toggle": "closing",
|
|
342
|
-
"sidebar.close": "closing"
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
closing: {
|
|
346
|
-
tags: ["moving"],
|
|
347
|
-
after: { [MOTION_DURATION]: "closed" },
|
|
348
|
-
on: {
|
|
349
|
-
"layout.transitioned": "closed",
|
|
350
|
-
"sidebar.toggle": "opening"
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
|
-
preview: {
|
|
356
|
-
initial: "single",
|
|
357
|
-
states: {
|
|
358
|
-
single: { on: { "preview.toggle": "splitting" } },
|
|
359
|
-
splitting: {
|
|
360
|
-
tags: ["split", "moving"],
|
|
361
|
-
after: { [MOTION_DURATION]: "split" },
|
|
362
|
-
on: {
|
|
363
|
-
"layout.transitioned": "split",
|
|
364
|
-
"preview.toggle": "unsplitting",
|
|
365
|
-
"sidebar.close": "unsplitting",
|
|
366
|
-
"sidebar.toggle": {
|
|
367
|
-
guard: "sidebarShown",
|
|
368
|
-
target: "unsplitting"
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
split: {
|
|
373
|
-
tags: ["split"],
|
|
374
|
-
on: {
|
|
375
|
-
"preview.toggle": "unsplitting",
|
|
376
|
-
"sidebar.close": "unsplitting",
|
|
377
|
-
"sidebar.toggle": {
|
|
378
|
-
guard: "sidebarShown",
|
|
379
|
-
target: "unsplitting"
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
},
|
|
383
|
-
unsplitting: {
|
|
384
|
-
tags: ["moving"],
|
|
385
|
-
after: { [MOTION_DURATION]: "single" },
|
|
386
|
-
on: {
|
|
387
|
-
"layout.transitioned": "single",
|
|
388
|
-
"preview.toggle": "splitting"
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
571
|
theme: {
|
|
394
572
|
initial: "applied",
|
|
395
573
|
states: {
|
|
@@ -436,13 +614,20 @@ const themerMachine = setup({
|
|
|
436
614
|
},
|
|
437
615
|
target: "switching"
|
|
438
616
|
},
|
|
439
|
-
"theme.import": "switching"
|
|
617
|
+
"theme.import": "switching",
|
|
618
|
+
"themes.sync": {
|
|
619
|
+
guard: {
|
|
620
|
+
type: "appliesAnotherTheme",
|
|
621
|
+
params: ({ event }) => ({ state: event.state })
|
|
622
|
+
},
|
|
623
|
+
target: "switching"
|
|
624
|
+
}
|
|
440
625
|
} },
|
|
441
626
|
switching: {
|
|
442
627
|
tags: ["switching"],
|
|
443
|
-
after: {
|
|
628
|
+
after: { 2e3: "applied" },
|
|
444
629
|
on: {
|
|
445
|
-
"
|
|
630
|
+
"theme.update": "applied",
|
|
446
631
|
"theme.pick": {
|
|
447
632
|
guard: {
|
|
448
633
|
type: "isAnotherTheme",
|
|
@@ -494,6 +679,14 @@ const themerMachine = setup({
|
|
|
494
679
|
"theme.import": {
|
|
495
680
|
target: "switching",
|
|
496
681
|
reenter: !0
|
|
682
|
+
},
|
|
683
|
+
"themes.sync": {
|
|
684
|
+
guard: {
|
|
685
|
+
type: "appliesAnotherTheme",
|
|
686
|
+
params: ({ event }) => ({ state: event.state })
|
|
687
|
+
},
|
|
688
|
+
target: "switching",
|
|
689
|
+
reenter: !0
|
|
497
690
|
}
|
|
498
691
|
}
|
|
499
692
|
}
|
|
@@ -505,7 +698,7 @@ const themerMachine = setup({
|
|
|
505
698
|
"theme.pick": { actions: [{
|
|
506
699
|
type: "pick",
|
|
507
700
|
params: ({ event }) => ({ slug: event.slug })
|
|
508
|
-
}
|
|
701
|
+
}] },
|
|
509
702
|
"theme.add": {
|
|
510
703
|
target: ".edit",
|
|
511
704
|
actions: [{
|
|
@@ -516,14 +709,14 @@ const themerMachine = setup({
|
|
|
516
709
|
palette: event.palette,
|
|
517
710
|
imageUrl: event.imageUrl
|
|
518
711
|
})
|
|
519
|
-
}
|
|
712
|
+
}]
|
|
520
713
|
},
|
|
521
714
|
"theme.duplicate": {
|
|
522
715
|
target: ".edit",
|
|
523
716
|
actions: [{
|
|
524
717
|
type: "duplicate",
|
|
525
718
|
params: ({ event }) => ({ slug: event.slug })
|
|
526
|
-
}
|
|
719
|
+
}]
|
|
527
720
|
},
|
|
528
721
|
"theme.edit": {
|
|
529
722
|
target: ".edit",
|
|
@@ -534,55 +727,51 @@ const themerMachine = setup({
|
|
|
534
727
|
actions: [{
|
|
535
728
|
type: "startEditing",
|
|
536
729
|
params: ({ event }) => ({ slug: event.slug })
|
|
537
|
-
}
|
|
730
|
+
}]
|
|
538
731
|
},
|
|
539
|
-
"theme.update": { actions: [
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
{
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
})
|
|
553
|
-
},
|
|
554
|
-
"persist"
|
|
555
|
-
] },
|
|
732
|
+
"theme.update": { actions: [{
|
|
733
|
+
type: "revokeImage",
|
|
734
|
+
params: ({ context, event }) => ({ url: event.imageUrl ? context.images[event.slug] : void 0 })
|
|
735
|
+
}, {
|
|
736
|
+
type: "update",
|
|
737
|
+
params: ({ event }) => ({
|
|
738
|
+
slug: event.slug,
|
|
739
|
+
title: event.title,
|
|
740
|
+
options: event.options,
|
|
741
|
+
palette: event.palette,
|
|
742
|
+
imageUrl: event.imageUrl
|
|
743
|
+
})
|
|
744
|
+
}] },
|
|
556
745
|
"theme.remove": { actions: [{
|
|
557
746
|
type: "remove",
|
|
558
747
|
params: ({ event }) => ({ slug: event.slug })
|
|
559
|
-
}
|
|
748
|
+
}] },
|
|
560
749
|
"theme.restore": { actions: [{
|
|
561
750
|
type: "restore",
|
|
562
751
|
params: ({ event }) => ({ slug: event.slug })
|
|
563
|
-
}
|
|
564
|
-
"theme.delete": { actions: [
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
{
|
|
570
|
-
|
|
571
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
572
|
-
},
|
|
573
|
-
"persist"
|
|
574
|
-
] },
|
|
752
|
+
}] },
|
|
753
|
+
"theme.delete": { actions: [{
|
|
754
|
+
type: "revokeImage",
|
|
755
|
+
params: ({ context, event }) => ({ url: context.images[event.slug] })
|
|
756
|
+
}, {
|
|
757
|
+
type: "delete",
|
|
758
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
759
|
+
}] },
|
|
575
760
|
"theme.reorder": { actions: [{
|
|
576
761
|
type: "reorder",
|
|
577
762
|
params: ({ event }) => ({ order: event.order })
|
|
578
|
-
}
|
|
763
|
+
}] },
|
|
579
764
|
"theme.import": { actions: [{
|
|
580
765
|
type: "import",
|
|
581
766
|
params: ({ event }) => ({
|
|
582
767
|
title: event.title,
|
|
583
768
|
options: event.options
|
|
584
769
|
})
|
|
585
|
-
}
|
|
770
|
+
}] },
|
|
771
|
+
"themes.sync": { actions: {
|
|
772
|
+
type: "sync",
|
|
773
|
+
params: ({ event }) => ({ state: event.state })
|
|
774
|
+
} },
|
|
586
775
|
"flow.list": ".list",
|
|
587
776
|
"flow.removed": {
|
|
588
777
|
target: ".removed",
|
|
@@ -590,7 +779,26 @@ const themerMachine = setup({
|
|
|
590
779
|
}
|
|
591
780
|
},
|
|
592
781
|
states: {
|
|
593
|
-
list: {
|
|
782
|
+
list: {
|
|
783
|
+
initial: "idle",
|
|
784
|
+
states: {
|
|
785
|
+
idle: { on: {
|
|
786
|
+
"dialog.paste": "pasting",
|
|
787
|
+
"dialog.snippet": {
|
|
788
|
+
guard: "hasAppliedTheme",
|
|
789
|
+
target: "snippet"
|
|
790
|
+
}
|
|
791
|
+
} },
|
|
792
|
+
pasting: { on: { "dialog.close": "idle" } },
|
|
793
|
+
snippet: {
|
|
794
|
+
on: { "dialog.close": "idle" },
|
|
795
|
+
always: {
|
|
796
|
+
guard: not("hasAppliedTheme"),
|
|
797
|
+
target: "idle"
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
},
|
|
594
802
|
edit: {
|
|
595
803
|
exit: "stopEditing",
|
|
596
804
|
always: {
|
|
@@ -616,596 +824,416 @@ function selectStoredState(snapshot) {
|
|
|
616
824
|
order
|
|
617
825
|
};
|
|
618
826
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
827
|
+
/** The channel every tab of the same origin shares — one Studio, one set of themes */
|
|
828
|
+
const PERSIST_TIMEOUT = 1e3;
|
|
829
|
+
function openBroadcastChannel() {
|
|
830
|
+
if (typeof BroadcastChannel != "function") return null;
|
|
831
|
+
let channel = new BroadcastChannel("sanity-themer");
|
|
832
|
+
return {
|
|
833
|
+
post: (message) => channel.postMessage(message),
|
|
834
|
+
listen: (listener) => {
|
|
835
|
+
let onMessage = (event) => listener(event.data);
|
|
836
|
+
return channel.addEventListener("message", onMessage), () => channel.removeEventListener("message", onMessage);
|
|
837
|
+
},
|
|
838
|
+
close: () => channel.close()
|
|
839
|
+
};
|
|
623
840
|
}
|
|
624
|
-
function
|
|
625
|
-
return
|
|
841
|
+
function browserLocks() {
|
|
842
|
+
return typeof navigator < "u" && "locks" in navigator ? navigator.locks : void 0;
|
|
626
843
|
}
|
|
627
|
-
function
|
|
628
|
-
|
|
844
|
+
function scheduleIdle(callback) {
|
|
845
|
+
if (typeof requestIdleCallback == "function") {
|
|
846
|
+
let id = requestIdleCallback(callback, { timeout: PERSIST_TIMEOUT });
|
|
847
|
+
return () => cancelIdleCallback(id);
|
|
848
|
+
}
|
|
849
|
+
let id = setTimeout(callback, PERSIST_TIMEOUT);
|
|
850
|
+
return () => clearTimeout(id);
|
|
629
851
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
852
|
+
function parseMessage(data) {
|
|
853
|
+
if (!data || typeof data != "object") return null;
|
|
854
|
+
let type = Reflect.get(data, "type");
|
|
855
|
+
if (type === "hello") {
|
|
856
|
+
let from = Reflect.get(data, "from");
|
|
857
|
+
return typeof from == "string" ? {
|
|
858
|
+
type,
|
|
859
|
+
from
|
|
860
|
+
} : null;
|
|
861
|
+
}
|
|
862
|
+
if (type === "state") {
|
|
863
|
+
let state = parseThemerState(Reflect.get(data, "state")), revision = Reflect.get(data, "revision"), from = Reflect.get(data, "from"), to = Reflect.get(data, "to");
|
|
864
|
+
return !state || typeof revision != "number" || !Number.isFinite(revision) || typeof from != "string" ? null : {
|
|
865
|
+
type,
|
|
866
|
+
state,
|
|
867
|
+
revision,
|
|
868
|
+
from,
|
|
869
|
+
...typeof to == "string" ? { to } : {}
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
function tabId() {
|
|
875
|
+
return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
638
876
|
}
|
|
639
877
|
/**
|
|
640
|
-
*
|
|
641
|
-
*
|
|
878
|
+
* Keeps the themes of every tab of the Studio the same, and persists them
|
|
879
|
+
* from one tab only.
|
|
642
880
|
*
|
|
643
|
-
*
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
651
|
-
* breakpoint or a custom navbar gives it.
|
|
881
|
+
* Every tab runs its own machine, and what the machine persists — the
|
|
882
|
+
* applied theme, the user's themes, what was removed and reordered — goes
|
|
883
|
+
* around to the other tabs on a `BroadcastChannel` as it changes, where it
|
|
884
|
+
* reaches their machines as a `themes.sync` event: the same themes
|
|
885
|
+
* everywhere, with the flow each tab is in, the tool's open and split state
|
|
886
|
+
* (the reducer's, in `plugin.tsx`) and the images of this session staying
|
|
887
|
+
* the tab's own. A tab that starts asks the others for the current state,
|
|
888
|
+
* in case what it read from storage was not written yet.
|
|
652
889
|
*
|
|
653
|
-
*
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (!root) return;
|
|
660
|
-
let navbar = null, resizeObserver = new ResizeObserver(() => {
|
|
661
|
-
navbar && setHeight(navbar.getBoundingClientRect().height);
|
|
662
|
-
}), observeNavbar = () => {
|
|
663
|
-
let next = root.querySelector(NAVBAR_SELECTOR);
|
|
664
|
-
next !== navbar && (navbar && resizeObserver.unobserve(navbar), navbar = next, navbar ? (resizeObserver.observe(navbar), setHeight(navbar.getBoundingClientRect().height)) : setHeight(null));
|
|
665
|
-
}, mutationObserver = new MutationObserver(observeNavbar);
|
|
666
|
-
return mutationObserver.observe(root, {
|
|
667
|
-
childList: !0,
|
|
668
|
-
subtree: !0
|
|
669
|
-
}), observeNavbar(), () => {
|
|
670
|
-
mutationObserver.disconnect(), resizeObserver.disconnect();
|
|
671
|
-
};
|
|
672
|
-
}, t1 = [ref], $[0] = ref, $[1] = t0, $[2] = t1), useEffect(t0, t1), height;
|
|
673
|
-
}
|
|
674
|
-
/**
|
|
675
|
-
* `useDeferredValue` with a transition type: the returned value lags behind
|
|
676
|
-
* `value` like a deferred one — rendered in a transition, which React
|
|
677
|
-
* animates — and the view transition React starts for it carries the given
|
|
678
|
-
* type (`document.startViewTransition({types})`), so a stylesheet tells it
|
|
679
|
-
* from any other transition on the page through
|
|
680
|
-
* `:active-view-transition-type()`.
|
|
890
|
+
* Persisting is the business of one tab at a time: the one holding the Web
|
|
891
|
+
* Lock, which it keeps for as long as it lives. The others queue for it, so
|
|
892
|
+
* the next tab takes over as the holder goes away, and a tab that takes
|
|
893
|
+
* over persists right away, in case the last one left with a write pending.
|
|
894
|
+
* Writes happen when the browser is idle — a run of edits is written once —
|
|
895
|
+
* and as the page hides, so that nothing pending is lost.
|
|
681
896
|
*
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
* it, with a state update of its own for the type to belong to: React renders
|
|
685
|
-
* every pending transition in one go, and the commit — the view transition
|
|
686
|
-
* — carries the types of all of them. The value itself stays with
|
|
687
|
-
* `useDeferredValue`, which reads the latest `value` as the deferred render
|
|
688
|
-
* runs: should `value` change back before that render commits, nothing
|
|
689
|
-
* changes and nothing animates — a queued state update would commit the
|
|
690
|
-
* stale value and animate it, only to animate back.
|
|
897
|
+
* Returns what stops the sync: it lets go of the lock, which hands the
|
|
898
|
+
* persisting on, and closes the channel.
|
|
691
899
|
*
|
|
692
900
|
* @internal
|
|
693
901
|
*/
|
|
694
|
-
function
|
|
695
|
-
let
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
902
|
+
function syncThemer(actorRef, options) {
|
|
903
|
+
let { persist, openChannel = openBroadcastChannel, schedule = scheduleIdle } = options, channel = openChannel(), locks = channel ? "locks" in options ? options.locks : browserLocks() : void 0, id = tabId(), abort = new AbortController(), leader = !1, cancelPersist, applying = !1, shared = selectStoredState(actorRef.getSnapshot()), revision = 0, origin = id, unchanged = !0, unanswered = /* @__PURE__ */ new Set(), isNewer = (message) => message.revision > revision || message.revision === revision && message.from > origin, persistNow = () => {
|
|
904
|
+
cancelPersist?.(), cancelPersist = void 0, persist(actorRef.getPersistedSnapshot());
|
|
905
|
+
}, persistSoon = () => {
|
|
906
|
+
cancelPersist?.(), cancelPersist = schedule(persistNow);
|
|
907
|
+
}, answer = (to) => {
|
|
908
|
+
channel?.post({
|
|
909
|
+
type: "state",
|
|
910
|
+
state: shared,
|
|
911
|
+
revision,
|
|
912
|
+
from: origin,
|
|
913
|
+
to
|
|
699
914
|
});
|
|
700
|
-
},
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
|
|
915
|
+
}, becomeLeader = () => {
|
|
916
|
+
leader = !0, persistNow();
|
|
917
|
+
for (let from of unanswered) answer(from);
|
|
918
|
+
unanswered.clear();
|
|
919
|
+
};
|
|
920
|
+
locks ? locks.request("sanity-themer:persist", { signal: abort.signal }, () => (becomeLeader(), new Promise((resolve) => {
|
|
921
|
+
abort.signal.addEventListener("abort", () => resolve(), { once: !0 });
|
|
922
|
+
}))).catch(() => {}) : becomeLeader();
|
|
923
|
+
let subscription = actorRef.subscribe((snapshot) => {
|
|
924
|
+
if (leader && persistSoon(), applying) return;
|
|
925
|
+
let state = selectStoredState(snapshot);
|
|
926
|
+
dequal(state, shared) || (shared = state, unchanged = !1, revision += 1, origin = id, channel?.post({
|
|
927
|
+
type: "state",
|
|
928
|
+
state,
|
|
929
|
+
revision,
|
|
930
|
+
from: id
|
|
931
|
+
}));
|
|
932
|
+
});
|
|
933
|
+
channel && (channel.listen((data) => {
|
|
934
|
+
let message = parseMessage(data);
|
|
935
|
+
if (message) {
|
|
936
|
+
if (message.type === "hello") {
|
|
937
|
+
leader ? answer(message.from) : unanswered.add(message.from);
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
if (message.to !== void 0) {
|
|
941
|
+
if (message.to !== id) return;
|
|
942
|
+
if (!unchanged) {
|
|
943
|
+
if (origin === id) {
|
|
944
|
+
revision = Math.max(revision, message.revision) + 1, channel.post({
|
|
945
|
+
type: "state",
|
|
946
|
+
state: shared,
|
|
947
|
+
revision,
|
|
948
|
+
from: id
|
|
949
|
+
});
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
if (!isNewer(message)) return;
|
|
953
|
+
}
|
|
954
|
+
} else if (!isNewer(message)) return;
|
|
955
|
+
if (revision = message.revision, origin = message.from, !dequal(message.state, shared)) {
|
|
956
|
+
shared = message.state, unchanged = !1, applying = !0;
|
|
957
|
+
try {
|
|
958
|
+
actorRef.send({
|
|
959
|
+
type: "themes.sync",
|
|
960
|
+
state: message.state
|
|
961
|
+
});
|
|
962
|
+
} finally {
|
|
963
|
+
applying = !1;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}), channel.post({
|
|
968
|
+
type: "hello",
|
|
969
|
+
from: id
|
|
970
|
+
}));
|
|
971
|
+
let flush = () => {
|
|
972
|
+
leader && cancelPersist && persistNow();
|
|
973
|
+
}, page = typeof window > "u" ? void 0 : window;
|
|
974
|
+
return page?.addEventListener("pagehide", flush), () => {
|
|
975
|
+
subscription.unsubscribe(), page?.removeEventListener("pagehide", flush), flush(), abort.abort(), channel?.close();
|
|
976
|
+
};
|
|
708
977
|
}
|
|
709
|
-
var layout = "_1yq4ar78", layoutTransitionType = "sanity-themer", panelTransitionClasses = {
|
|
710
|
-
slideIn: "_1yq4ar76",
|
|
711
|
-
slideOut: "_1yq4ar77"
|
|
712
|
-
}, splitTransitionClasses = {
|
|
713
|
-
resize: "_1yq4ar70",
|
|
714
|
-
crossfade: "_1yq4ar71",
|
|
715
|
-
slideIn: "_1yq4ar72",
|
|
716
|
-
slideOut: "_1yq4ar73",
|
|
717
|
-
dropIn: "_1yq4ar74",
|
|
718
|
-
dropOut: "_1yq4ar75"
|
|
719
|
-
}, studioScheme = {
|
|
720
|
-
light: "_1yq4ar79",
|
|
721
|
-
dark: "_1yq4ar7a"
|
|
722
|
-
};
|
|
723
978
|
/**
|
|
724
|
-
*
|
|
725
|
-
* the
|
|
726
|
-
*
|
|
979
|
+
* Persists the machine's snapshot for the next session, and keeps the themes
|
|
980
|
+
* in step with the other tabs of the Studio — from the one tab holding the
|
|
981
|
+
* Web Lock, when the browser is idle; see `sync.ts`. Rendered by the layout,
|
|
982
|
+
* outside the sidebar's `Activity`, so that it runs while the sidebar is
|
|
983
|
+
* closed too: other tabs change the themes whether this one shows them or
|
|
984
|
+
* not.
|
|
727
985
|
*/
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
return
|
|
731
|
-
}
|
|
732
|
-
function selectView(snapshot) {
|
|
733
|
-
let { editing } = snapshot.context;
|
|
734
|
-
return snapshot.matches({ flow: "edit" }) && editing ? {
|
|
735
|
-
name: "edit",
|
|
736
|
-
slug: editing.slug,
|
|
737
|
-
focusTitle: editing.focusTitle
|
|
738
|
-
} : snapshot.matches({ flow: "removed" }) ? { name: "removed" } : { name: "list" };
|
|
739
|
-
}
|
|
740
|
-
function sameView(a, b) {
|
|
741
|
-
return a.name === b.name ? a.name !== "edit" || b.name !== "edit" || a.slug === b.slug && a.focusTitle === b.focusTitle : !1;
|
|
986
|
+
function PersistActorSnapshot(t0) {
|
|
987
|
+
let $ = c(3), { actorRef } = t0, t1, t2;
|
|
988
|
+
return $[0] === actorRef ? (t1 = $[1], t2 = $[2]) : (t1 = () => syncThemer(actorRef, { persist: writePersistedSnapshot }), t2 = [actorRef], $[0] = actorRef, $[1] = t1, $[2] = t2), useEffect(t1, t2), null;
|
|
742
989
|
}
|
|
743
990
|
/**
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
|
|
991
|
+
* The sidebar — everything in it, from the theme list to the snippet dialog —
|
|
992
|
+
* loads the first time it opens. There is no `Suspense` boundary around it,
|
|
993
|
+
* so it must only ever mount from a transition: the navbar toggle prerenders
|
|
994
|
+
* it in one as the pointer comes near, which keeps the Studio as it was while
|
|
995
|
+
* the code loads, where an urgent render would suspend up to the Studio's
|
|
996
|
+
* own boundary and swap the whole Studio for its loading screen.
|
|
997
|
+
*/
|
|
998
|
+
const ResizableSidebar = lazy(() => import("./ResizableSidebar-C1nr3Ls2.js"));
|
|
999
|
+
/**
|
|
1000
|
+
* Wraps the whole Studio to run the themer machine that the sidebar's flows
|
|
1001
|
+
* share — the user's themes, which one is applied and which flow the sidebar
|
|
1002
|
+
* is in — and renders the sidebar next to the Studio. The sidebar sits at
|
|
1003
|
+
* the `layout` level rather than in `activeToolLayout` because the split
|
|
1004
|
+
* preview renders the Studio twice, and the sidebar must not come along.
|
|
751
1005
|
*
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
*
|
|
1006
|
+
* Whether the sidebar is open and whether the Studio shows twice comes in as
|
|
1007
|
+
* props from the tool reducer in `ThemerProvider`, and goes on to the sidebar
|
|
1008
|
+
* as props along with the machine's actor, which the sidebar selects its
|
|
1009
|
+
* themes and flow from (see `ThemerProps`): the navbar toggle and the sidebar
|
|
1010
|
+
* dispatch to the reducer inside transitions tagged with a transition type
|
|
1011
|
+
* (see `addThemerTransitionType`), and the `ViewTransition` boundaries below
|
|
1012
|
+
* pick their classes by that type, so React animates the sidebar sliding in
|
|
1013
|
+
* and out, the split copy coming and going and a picked theme cross-fading
|
|
1014
|
+
* in (React 19.3), styled in `ViewTransitions.css.ts`. The sidebar and the
|
|
1015
|
+
* split copy stay mounted while hidden, in `Activity`, so that opening them
|
|
1016
|
+
* again is instant and they can prerender ahead of the transition. The
|
|
1017
|
+
* navbar toggle, which the Studio renders somewhere in `children`, reads the
|
|
1018
|
+
* same values and the plugin's options from the contexts
|
|
1019
|
+
* `ThemerNavbarProvider` provides in each copy of the Studio, and measures
|
|
1020
|
+
* the Studio navbar for the sidebar's header — in the Studio proper, not in
|
|
1021
|
+
* the split copy, which `ToolShouldDetectNavbarHeightContext` tells apart.
|
|
757
1022
|
*
|
|
758
|
-
* The
|
|
759
|
-
* `
|
|
760
|
-
*
|
|
761
|
-
*
|
|
1023
|
+
* The applied theme is the reducer's, provided above this layout in
|
|
1024
|
+
* `ThemerProvider`: the machine publishes it synchronously, which React does
|
|
1025
|
+
* not animate, so `ThemerThemeCrossfader` hands it to the reducer in a
|
|
1026
|
+
* transition of its own. The Studio next to the sidebar always follows the
|
|
1027
|
+
* appearance setting (light/dark/system) and the picked theme like any other
|
|
1028
|
+
* theme would; the split preview adds a second copy in the opposite scheme
|
|
1029
|
+
* on the far side.
|
|
762
1030
|
*
|
|
763
1031
|
* Whatever else on the page has a `view-transition-name` — an avatar the
|
|
764
1032
|
* Studio names so it moves as one piece — comes along in step: every
|
|
765
1033
|
* transition the layout starts carries `layoutTransitionType`, which the
|
|
766
1034
|
* stylesheet keys on to move every group in the layout's time (see
|
|
767
|
-
* `
|
|
1035
|
+
* `ViewTransitions.css.ts`).
|
|
768
1036
|
*
|
|
769
1037
|
* @internal
|
|
770
1038
|
*/
|
|
771
|
-
function ThemerLayout(
|
|
772
|
-
let $ = c(
|
|
773
|
-
$[0] ===
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
let open = t2, t3;
|
|
782
|
-
$[6] === snapshot ? t3 = $[7] : (t3 = snapshot.hasTag("split"), $[6] = snapshot, $[7] = t3);
|
|
783
|
-
let split = t3, t4;
|
|
784
|
-
$[8] === snapshot ? t4 = $[9] : (t4 = snapshot.hasTag("moving"), $[8] = snapshot, $[9] = t4);
|
|
785
|
-
let moving = t4, t5;
|
|
786
|
-
$[10] === snapshot ? t5 = $[11] : (t5 = snapshot.hasTag("switching"), $[10] = snapshot, $[11] = t5);
|
|
787
|
-
let switching = t5, { images } = snapshot.context, mobile = useMediaIndex() <= 1, studioRef = useRef(null), navbarHeight = useStudioNavbarHeight(studioRef), reduceMotion = usePrefersReducedMotion$1(), deferredOpen = useTypedDeferredValue(reduceMotion ? null : open, layoutTransitionType), deferredSplit = useTypedDeferredValue(reduceMotion ? null : split, layoutTransitionType), shownOpen = deferredOpen ?? open, shownSplit = deferredSplit ?? split, [panelShown, setPanelShown] = useState(!1), panelWanted = open || panelShown, panelMounted = useTypedDeferredValue(panelWanted, layoutTransitionType);
|
|
788
|
-
panelMounted && !panelShown && setPanelShown(!0);
|
|
789
|
-
let loading = panelWanted !== panelMounted, t6;
|
|
790
|
-
$[12] !== baseOptions || $[13] !== stored ? (t6 = resolveThemes(stored, baseOptions), $[12] = baseOptions, $[13] = stored, $[14] = t6) : t6 = $[14];
|
|
791
|
-
let { themes, removed, active } = t6, activeOptions = active.source === "config" ? null : active.options, t7;
|
|
792
|
-
$[15] === activeOptions ? t7 = $[16] : (t7 = activeOptions === null ? null : buildTheme(activeOptions), $[15] = activeOptions, $[16] = t7);
|
|
793
|
-
let theme = t7, deferredTheme = useTypedDeferredValue(reduceMotion ? void 0 : theme, layoutTransitionType), shownTheme = deferredTheme === void 0 ? theme : deferredTheme, scheme = useColorSchemeValue(), oppositeScheme = scheme === "dark" ? "light" : "dark", t8;
|
|
794
|
-
$[17] !== scheme || $[18] !== shownTheme?.v2?.color ? (t8 = () => {
|
|
795
|
-
let background = shownTheme?.v2?.color[scheme].default.bg;
|
|
796
|
-
if (background === void 0) return;
|
|
797
|
-
let { style } = document.body, previous = style.backgroundColor;
|
|
798
|
-
return style.backgroundColor = background, () => {
|
|
799
|
-
style.backgroundColor = previous;
|
|
800
|
-
};
|
|
801
|
-
}, $[17] = scheme, $[18] = shownTheme?.v2?.color, $[19] = t8) : t8 = $[19];
|
|
802
|
-
let t9;
|
|
803
|
-
$[20] !== scheme || $[21] !== shownTheme ? (t9 = [scheme, shownTheme], $[20] = scheme, $[21] = shownTheme, $[22] = t9) : t9 = $[22], useEffect(t8, t9);
|
|
804
|
-
let t10, t11;
|
|
805
|
-
$[23] === actorRef ? (t10 = $[24], t11 = $[25]) : (t10 = () => () => {
|
|
806
|
-
for (let url of Object.values(actorRef.getSnapshot().context.images)) URL.revokeObjectURL(url);
|
|
807
|
-
}, t11 = [actorRef], $[23] = actorRef, $[24] = t10, $[25] = t11), useEffect(t10, t11);
|
|
808
|
-
let t12;
|
|
809
|
-
$[26] !== active || $[27] !== baseOptions || $[28] !== images || $[29] !== loading || $[30] !== mobile || $[31] !== navbarHeight || $[32] !== open || $[33] !== removed || $[34] !== send || $[35] !== split || $[36] !== themes || $[37] !== view ? (t12 = {
|
|
810
|
-
baseOptions,
|
|
811
|
-
themes,
|
|
812
|
-
removed,
|
|
813
|
-
active,
|
|
814
|
-
images,
|
|
815
|
-
view,
|
|
1039
|
+
function ThemerLayout(t0) {
|
|
1040
|
+
let $ = c(50), { children, backgroundColor, config, dispatch, navbarHeight, open, persistedSnapshot, prerender, prerenderSplitScreen, scheme, split } = t0, t1;
|
|
1041
|
+
$[0] === persistedSnapshot ? t1 = $[1] : (t1 = { snapshot: persistedSnapshot }, $[0] = persistedSnapshot, $[1] = t1);
|
|
1042
|
+
let actorRef = useActorRef(themerMachine, t1), oppositeScheme = scheme === "dark" ? "light" : "dark", t2;
|
|
1043
|
+
$[2] === backgroundColor ? t2 = $[3] : (t2 = { backgroundColor }, $[2] = backgroundColor, $[3] = t2);
|
|
1044
|
+
let t3 = split && open ? "visible" : "hidden", t4;
|
|
1045
|
+
$[4] !== children || $[5] !== config || $[6] !== dispatch || $[7] !== open || $[8] !== oppositeScheme || $[9] !== prerenderSplitScreen || $[10] !== split ? (t4 = prerenderSplitScreen && /* @__PURE__ */ jsx(StudioPreview, {
|
|
1046
|
+
borderRight: !0,
|
|
1047
|
+
config,
|
|
1048
|
+
dispatch,
|
|
816
1049
|
open,
|
|
817
|
-
|
|
1050
|
+
scheme: oppositeScheme,
|
|
818
1051
|
split,
|
|
819
|
-
|
|
1052
|
+
children
|
|
1053
|
+
}), $[4] = children, $[5] = config, $[6] = dispatch, $[7] = open, $[8] = oppositeScheme, $[9] = prerenderSplitScreen, $[10] = split, $[11] = t4) : t4 = $[11];
|
|
1054
|
+
let t5;
|
|
1055
|
+
$[12] === t4 ? t5 = $[13] : (t5 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
1056
|
+
default: "none",
|
|
1057
|
+
enter: viewTransitionClasses.splitscreen.enter,
|
|
1058
|
+
update: viewTransitionClasses.splitscreen.update,
|
|
1059
|
+
exit: viewTransitionClasses.splitscreen.exit,
|
|
1060
|
+
children: t4
|
|
1061
|
+
}), $[12] = t4, $[13] = t5);
|
|
1062
|
+
let t6;
|
|
1063
|
+
$[14] !== t3 || $[15] !== t5 ? (t6 = /* @__PURE__ */ jsx(Activity, {
|
|
1064
|
+
mode: t3,
|
|
1065
|
+
children: t5
|
|
1066
|
+
}), $[14] = t3, $[15] = t5, $[16] = t6) : t6 = $[16];
|
|
1067
|
+
let t7;
|
|
1068
|
+
$[17] !== children || $[18] !== config || $[19] !== dispatch || $[20] !== open || $[21] !== split ? (t7 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
1069
|
+
default: "none",
|
|
1070
|
+
update: viewTransitionClasses.studio.update,
|
|
1071
|
+
children: /* @__PURE__ */ jsx(StudioPreview, {
|
|
1072
|
+
config,
|
|
1073
|
+
dispatch,
|
|
1074
|
+
open,
|
|
1075
|
+
shouldDetectNavbarHeight: !0,
|
|
1076
|
+
split,
|
|
1077
|
+
children
|
|
1078
|
+
})
|
|
1079
|
+
}), $[17] = children, $[18] = config, $[19] = dispatch, $[20] = open, $[21] = split, $[22] = t7) : t7 = $[22];
|
|
1080
|
+
let t8 = open ? "visible" : "hidden", t9;
|
|
1081
|
+
$[23] !== actorRef || $[24] !== dispatch || $[25] !== navbarHeight || $[26] !== prerender || $[27] !== split ? (t9 = prerender && /* @__PURE__ */ jsx(ResizableSidebar, {
|
|
1082
|
+
actorRef,
|
|
1083
|
+
dispatch,
|
|
820
1084
|
navbarHeight,
|
|
821
|
-
|
|
822
|
-
}, $[
|
|
823
|
-
let
|
|
824
|
-
$[
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
update: switching ? splitTransitionClasses.crossfade : "none",
|
|
839
|
-
studioUpdate: moving ? splitTransitionClasses.resize : switching ? splitTransitionClasses.crossfade : "none"
|
|
840
|
-
}, $[41] = mobile, $[42] = moving, $[43] = reduceMotion, $[44] = switching, $[45] = t14) : t14 = $[45];
|
|
841
|
-
let classes = t14, t15 = mobile ? "column" : "row", t16;
|
|
842
|
-
$[46] !== classes.copyEnter || $[47] !== classes.copyExit || $[48] !== classes.update || $[49] !== mobile || $[50] !== oppositeScheme || $[51] !== shownOpen || $[52] !== shownSplit || $[53] !== shownTheme || $[54] !== studio || $[55] !== transitioned ? (t16 = shownOpen && /* @__PURE__ */ jsx(Activity, {
|
|
843
|
-
mode: shownSplit ? "visible" : "hidden",
|
|
844
|
-
children: /* @__PURE__ */ jsx(ViewTransition, {
|
|
845
|
-
enter: classes.copyEnter,
|
|
846
|
-
exit: classes.copyExit,
|
|
847
|
-
onEnter: transitioned,
|
|
848
|
-
onExit: transitioned,
|
|
849
|
-
onUpdate: transitioned,
|
|
850
|
-
update: classes.update,
|
|
851
|
-
children: /* @__PURE__ */ jsx(StudioPreview, {
|
|
852
|
-
borderBottom: mobile,
|
|
853
|
-
borderRight: !mobile,
|
|
854
|
-
scheme: oppositeScheme,
|
|
855
|
-
theme: shownTheme,
|
|
856
|
-
children: studio
|
|
857
|
-
})
|
|
858
|
-
}, "opposite")
|
|
859
|
-
}), $[46] = classes.copyEnter, $[47] = classes.copyExit, $[48] = classes.update, $[49] = mobile, $[50] = oppositeScheme, $[51] = shownOpen, $[52] = shownSplit, $[53] = shownTheme, $[54] = studio, $[55] = transitioned, $[56] = t16) : t16 = $[56];
|
|
860
|
-
let t17;
|
|
861
|
-
$[57] !== shownTheme || $[58] !== studio ? (t17 = /* @__PURE__ */ jsx(StudioPreview, {
|
|
862
|
-
ref: studioRef,
|
|
863
|
-
theme: shownTheme,
|
|
864
|
-
children: studio
|
|
865
|
-
}), $[57] = shownTheme, $[58] = studio, $[59] = t17) : t17 = $[59];
|
|
866
|
-
let t18;
|
|
867
|
-
$[60] !== classes.studioUpdate || $[61] !== t17 || $[62] !== transitioned ? (t18 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
868
|
-
onUpdate: transitioned,
|
|
869
|
-
update: classes.studioUpdate,
|
|
870
|
-
children: t17
|
|
871
|
-
}, "primary"), $[60] = classes.studioUpdate, $[61] = t17, $[62] = transitioned, $[63] = t18) : t18 = $[63];
|
|
872
|
-
let t19 = shownOpen ? "visible" : "hidden", t20 = shownTheme ?? void 0, t21;
|
|
873
|
-
$[64] !== mobile || $[65] !== panelMounted ? (t21 = panelMounted && /* @__PURE__ */ jsx(ResizableSidebar, { overlay: mobile }), $[64] = mobile, $[65] = panelMounted, $[66] = t21) : t21 = $[66];
|
|
874
|
-
let t22;
|
|
875
|
-
$[67] !== t20 || $[68] !== t21 ? (t22 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
876
|
-
theme: t20,
|
|
877
|
-
children: t21
|
|
878
|
-
}), $[67] = t20, $[68] = t21, $[69] = t22) : t22 = $[69];
|
|
879
|
-
let t23;
|
|
880
|
-
$[70] !== classes.panelEnter || $[71] !== classes.panelExit || $[72] !== classes.update || $[73] !== t22 || $[74] !== transitioned ? (t23 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
881
|
-
enter: classes.panelEnter,
|
|
882
|
-
exit: classes.panelExit,
|
|
883
|
-
onEnter: transitioned,
|
|
884
|
-
onExit: transitioned,
|
|
885
|
-
onUpdate: transitioned,
|
|
886
|
-
update: classes.update,
|
|
887
|
-
children: t22
|
|
888
|
-
}, "panel"), $[70] = classes.panelEnter, $[71] = classes.panelExit, $[72] = classes.update, $[73] = t22, $[74] = transitioned, $[75] = t23) : t23 = $[75];
|
|
889
|
-
let t24;
|
|
890
|
-
$[76] !== t19 || $[77] !== t23 ? (t24 = /* @__PURE__ */ jsx(Activity, {
|
|
891
|
-
mode: t19,
|
|
892
|
-
children: t23
|
|
893
|
-
}), $[76] = t19, $[77] = t23, $[78] = t24) : t24 = $[78];
|
|
894
|
-
let t25;
|
|
895
|
-
$[79] !== t15 || $[80] !== t16 || $[81] !== t18 || $[82] !== t24 ? (t25 = /* @__PURE__ */ jsxs(Flex, {
|
|
1085
|
+
split
|
|
1086
|
+
}), $[23] = actorRef, $[24] = dispatch, $[25] = navbarHeight, $[26] = prerender, $[27] = split, $[28] = t9) : t9 = $[28];
|
|
1087
|
+
let t10;
|
|
1088
|
+
$[29] === t9 ? t10 = $[30] : (t10 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
1089
|
+
default: "none",
|
|
1090
|
+
enter: viewTransitionClasses.sidebar.enter,
|
|
1091
|
+
update: viewTransitionClasses.sidebar.update,
|
|
1092
|
+
exit: viewTransitionClasses.sidebar.exit,
|
|
1093
|
+
children: t9
|
|
1094
|
+
}), $[29] = t9, $[30] = t10);
|
|
1095
|
+
let t11;
|
|
1096
|
+
$[31] !== t10 || $[32] !== t8 ? (t11 = /* @__PURE__ */ jsx(Activity, {
|
|
1097
|
+
mode: t8,
|
|
1098
|
+
children: t10
|
|
1099
|
+
}), $[31] = t10, $[32] = t8, $[33] = t11) : t11 = $[33];
|
|
1100
|
+
let t12;
|
|
1101
|
+
$[34] !== t11 || $[35] !== t2 || $[36] !== t6 || $[37] !== t7 ? (t12 = /* @__PURE__ */ jsxs(Flex, {
|
|
896
1102
|
className: layout,
|
|
897
|
-
direction:
|
|
1103
|
+
direction: "row",
|
|
898
1104
|
height: "fill",
|
|
899
1105
|
sizing: "border",
|
|
1106
|
+
style: t2,
|
|
900
1107
|
children: [
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1108
|
+
t6,
|
|
1109
|
+
t7,
|
|
1110
|
+
t11
|
|
904
1111
|
]
|
|
905
|
-
}), $[
|
|
906
|
-
let
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
}), $[
|
|
1112
|
+
}), $[34] = t11, $[35] = t2, $[36] = t6, $[37] = t7, $[38] = t12) : t12 = $[38];
|
|
1113
|
+
let t13;
|
|
1114
|
+
$[39] !== actorRef || $[40] !== dispatch ? (t13 = /* @__PURE__ */ jsx(ThemerThemeCrossfader, {
|
|
1115
|
+
actorRef,
|
|
1116
|
+
dispatch
|
|
1117
|
+
}), $[39] = actorRef, $[40] = dispatch, $[41] = t13) : t13 = $[41];
|
|
1118
|
+
let t14, t15;
|
|
1119
|
+
$[42] === actorRef ? (t14 = $[43], t15 = $[44]) : (t14 = /* @__PURE__ */ jsx(RevokeImageUrls, { actorRef }), t15 = /* @__PURE__ */ jsx(PersistActorSnapshot, { actorRef }), $[42] = actorRef, $[43] = t14, $[44] = t15);
|
|
1120
|
+
let t16;
|
|
1121
|
+
return $[45] !== t12 || $[46] !== t13 || $[47] !== t14 || $[48] !== t15 ? (t16 = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1122
|
+
t12,
|
|
1123
|
+
t13,
|
|
1124
|
+
t14,
|
|
1125
|
+
t15
|
|
1126
|
+
] }), $[45] = t12, $[46] = t13, $[47] = t14, $[48] = t15, $[49] = t16) : t16 = $[49], t16;
|
|
911
1127
|
}
|
|
912
1128
|
/**
|
|
913
1129
|
* One copy of the Studio, in the given color scheme — or, without one, in the
|
|
914
|
-
* scheme the Studio is showing. The
|
|
915
|
-
* the
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
* they stay in sync while navigating. The `color-scheme` of a forced scheme
|
|
921
|
-
* keeps native form controls and scrollbars in step with it.
|
|
1130
|
+
* scheme the Studio is showing. The two copies of the split preview share the
|
|
1131
|
+
* router, the document store and every other provider above the layout —
|
|
1132
|
+
* only the scheme differs — so they stay in sync while navigating. The
|
|
1133
|
+
* `color-scheme` of a forced scheme keeps native form controls and scrollbars
|
|
1134
|
+
* in step with it. Each copy provides the navbar toggle it renders with the
|
|
1135
|
+
* contexts it reads (see `ThemerNavbarProvider`).
|
|
922
1136
|
*/
|
|
923
|
-
function StudioPreview(
|
|
924
|
-
let $ = c(
|
|
925
|
-
$[0]
|
|
926
|
-
|
|
1137
|
+
function StudioPreview(t0) {
|
|
1138
|
+
let $ = c(17), { borderRight, children, config, dispatch, open, scheme, shouldDetectNavbarHeight, split } = t0, [boundaryElement, setBoundaryElement] = useState(null), t1;
|
|
1139
|
+
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t1 = (boundaryElement_0) => (startTransition(() => setBoundaryElement(boundaryElement_0)), _temp), $[0] = t1) : t1 = $[0];
|
|
1140
|
+
let t2;
|
|
1141
|
+
$[1] === children ? t2 = $[2] : (t2 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
1142
|
+
update: "none",
|
|
1143
|
+
children
|
|
1144
|
+
}), $[1] = children, $[2] = t2);
|
|
1145
|
+
let t3;
|
|
1146
|
+
$[3] !== config || $[4] !== dispatch || $[5] !== open || $[6] !== shouldDetectNavbarHeight || $[7] !== split || $[8] !== t2 ? (t3 = /* @__PURE__ */ jsx(ThemerNavbarProvider, {
|
|
1147
|
+
config,
|
|
1148
|
+
dispatch,
|
|
1149
|
+
open,
|
|
1150
|
+
shouldDetectNavbarHeight,
|
|
1151
|
+
split,
|
|
1152
|
+
children: t2
|
|
1153
|
+
}), $[3] = config, $[4] = dispatch, $[5] = open, $[6] = shouldDetectNavbarHeight, $[7] = split, $[8] = t2, $[9] = t3) : t3 = $[9];
|
|
1154
|
+
let t4;
|
|
1155
|
+
$[10] !== boundaryElement || $[11] !== t3 ? (t4 = /* @__PURE__ */ jsx(BoundaryElementProvider, {
|
|
1156
|
+
element: boundaryElement,
|
|
1157
|
+
children: t3
|
|
1158
|
+
}), $[10] = boundaryElement, $[11] = t3, $[12] = t4) : t4 = $[12];
|
|
1159
|
+
let t5;
|
|
1160
|
+
return $[13] !== borderRight || $[14] !== scheme || $[15] !== t4 ? (t5 = /* @__PURE__ */ jsx(Card, {
|
|
927
1161
|
borderRight,
|
|
928
|
-
className: t1,
|
|
929
1162
|
flex: 1,
|
|
930
1163
|
height: "fill",
|
|
931
1164
|
overflow: "hidden",
|
|
932
|
-
ref,
|
|
933
|
-
children
|
|
934
|
-
}), $[0] = borderBottom, $[1] = borderRight, $[2] = children, $[3] = ref, $[4] = t1, $[5] = t2) : t2 = $[5];
|
|
935
|
-
let t3;
|
|
936
|
-
return $[6] !== scheme || $[7] !== t0 || $[8] !== t2 ? (t3 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
937
1165
|
scheme,
|
|
938
|
-
|
|
939
|
-
children:
|
|
940
|
-
}), $[
|
|
1166
|
+
ref: t1,
|
|
1167
|
+
children: t4
|
|
1168
|
+
}), $[13] = borderRight, $[14] = scheme, $[15] = t4, $[16] = t5) : t5 = $[16], t5;
|
|
941
1169
|
}
|
|
1170
|
+
function _temp() {}
|
|
942
1171
|
/**
|
|
943
|
-
*
|
|
944
|
-
*
|
|
1172
|
+
* Hands the applied theme to the tool reducer as the machine publishes it —
|
|
1173
|
+
* in a transition tagged to cross-fade while the machine says a theme is
|
|
1174
|
+
* being switched to, so that editing the applied theme's colors applies live
|
|
1175
|
+
* while picking another theme fades the Studio over to it
|
|
945
1176
|
*/
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
function actAt(progress) {
|
|
957
|
-
return progress * TOTAL_ACTS;
|
|
958
|
-
}
|
|
959
|
-
/**
|
|
960
|
-
* How far the wheel has turned at `progress` (0–1), in degrees.
|
|
961
|
-
*
|
|
962
|
-
* It stands still while the top half of the slices pop in. As the 6 o'clock
|
|
963
|
-
* slice pops in it starts spinning — one full turn with a cubic ease-in, from
|
|
964
|
-
* rest to top speed as the bottom half pop in — and it stands still again the
|
|
965
|
-
* moment it is back upright: the clearing of {@link isSliceFilled} takes off
|
|
966
|
-
* at the speed the spin reached, as if the wheel kept turning and eased out
|
|
967
|
-
* over a second turn, so the two read as one movement.
|
|
968
|
-
*/
|
|
969
|
-
function wheelRotation(progress) {
|
|
970
|
-
let spin = actAt(progress) - 1;
|
|
971
|
-
return spin <= 0 || spin >= 1 ? 0 : 360 * spin ** 3;
|
|
972
|
-
}
|
|
973
|
-
/**
|
|
974
|
-
* Whether slice `index` (clockwise from 12 o'clock) shows its color at
|
|
975
|
-
* `progress` (0–1). A slice pops in whole as the fill reaches its leading
|
|
976
|
-
* edge, and pops out whole as the clearing does — stepped, never partial.
|
|
977
|
-
*
|
|
978
|
-
* The fill goes clockwise at a steady pace over the first two acts, half of
|
|
979
|
-
* the slices in each. The clearing follows in the third act, the same way
|
|
980
|
-
* round, on a cubic ease-out that starts at the speed the spin of
|
|
981
|
-
* {@link wheelRotation} ends with: the slices at first pop out as quickly as
|
|
982
|
-
* they were passing by, then ever more slowly.
|
|
983
|
-
*/
|
|
984
|
-
function isSliceFilled(progress, index) {
|
|
985
|
-
let act = actAt(progress);
|
|
986
|
-
if (act * 8 / 2 <= index) return !1;
|
|
987
|
-
let clearing = act - 2;
|
|
988
|
-
return clearing <= 0 || (1 - (1 - clearing) ** 3) * 8 < index;
|
|
989
|
-
}
|
|
990
|
-
/**
|
|
991
|
-
* The geometry of `ColorWheelIcon` from `@sanity/icons`: a ring between two
|
|
992
|
-
* circles around the center of a 25×25 viewBox, cut into eight slices by the
|
|
993
|
-
* spokes of its outline path
|
|
994
|
-
*/
|
|
995
|
-
const CENTER = 12.5, INNER_RADIUS = 3.5;
|
|
996
|
-
/** A point on a circle around the center, at `degrees` clockwise from 3 o'clock */
|
|
997
|
-
function point(radius, degrees) {
|
|
998
|
-
let radians = degrees * Math.PI / 180;
|
|
999
|
-
return `${CENTER + radius * Math.cos(radians)} ${CENTER + radius * Math.sin(radians)}`;
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* The part of the ring a slice colors in: between its two spokes, from the
|
|
1003
|
-
* inner circle out to the outer. The 12 o'clock slice is centered on 12, so
|
|
1004
|
-
* it starts half a slice before it.
|
|
1005
|
-
*/
|
|
1006
|
-
function slicePath(index) {
|
|
1007
|
-
let start = index * 45 - 45 / 2 - 90, end = start + 45;
|
|
1008
|
-
return [
|
|
1009
|
-
`M${point(8, start)}`,
|
|
1010
|
-
`A8 8 0 0 1 ${point(8, end)}`,
|
|
1011
|
-
`L${point(INNER_RADIUS, end)}`,
|
|
1012
|
-
`A${INNER_RADIUS} ${INNER_RADIUS} 0 0 0 ${point(INNER_RADIUS, start)}`,
|
|
1013
|
-
"Z"
|
|
1014
|
-
].join("");
|
|
1015
|
-
}
|
|
1016
|
-
const SLICE_PATHS = SLICE_COLORS.map((_, index) => slicePath(index));
|
|
1017
|
-
/**
|
|
1018
|
-
* A filled shape, not a stroked one: the icon styles of `@sanity/ui` force
|
|
1019
|
-
* `vector-effect: non-scaling-stroke` on every path in an icon, which fills
|
|
1020
|
-
* are none the wiser about
|
|
1021
|
-
*/
|
|
1022
|
-
function Slice(props) {
|
|
1023
|
-
let $ = c(8), { index, progress } = props, t0;
|
|
1024
|
-
$[0] === index ? t0 = $[1] : (t0 = (value) => +!!isSliceFilled(value, index), $[0] = index, $[1] = t0);
|
|
1025
|
-
let opacity = useTransform(progress, t0), t1 = SLICE_PATHS[index], t2 = SLICE_COLORS[index], t3;
|
|
1026
|
-
$[2] === opacity ? t3 = $[3] : (t3 = { opacity }, $[2] = opacity, $[3] = t3);
|
|
1027
|
-
let t4;
|
|
1028
|
-
return $[4] !== t1 || $[5] !== t2 || $[6] !== t3 ? (t4 = /* @__PURE__ */ jsx(motion.path, {
|
|
1029
|
-
d: t1,
|
|
1030
|
-
fill: t2,
|
|
1031
|
-
style: t3
|
|
1032
|
-
}), $[4] = t1, $[5] = t2, $[6] = t3, $[7] = t4) : t4 = $[7], t4;
|
|
1033
|
-
}
|
|
1034
|
-
/**
|
|
1035
|
-
* The `ColorWheelIcon` of `@sanity/icons`, with an animation driven by
|
|
1036
|
-
* `progress` (0–1): the slices pop in with color one at a time, clockwise
|
|
1037
|
-
* from 12 o'clock like the segments of a classic spinner, the wheel spins once
|
|
1038
|
-
* as the 6 o'clock slice pops in, and the colors pop out the same way round,
|
|
1039
|
-
* carrying the spin's speed to a rest (see `colorWheel.ts`). At rest — at
|
|
1040
|
-
* either end — it looks exactly like the icon it stands in for. With
|
|
1041
|
-
* `prefers-reduced-motion: reduce` the wheel keeps still; only the colors come
|
|
1042
|
-
* and go.
|
|
1043
|
-
*
|
|
1044
|
-
* @internal
|
|
1045
|
-
*/
|
|
1046
|
-
function AnimatedColorWheelIcon(props) {
|
|
1047
|
-
let $ = c(10), { progress } = props, prefersReducedMotion = usePrefersReducedMotion(), t0;
|
|
1048
|
-
$[0] === prefersReducedMotion ? t0 = $[1] : (t0 = (value) => prefersReducedMotion ? 0 : wheelRotation(value), $[0] = prefersReducedMotion, $[1] = t0);
|
|
1049
|
-
let rotate = useTransform(progress, t0), t1;
|
|
1050
|
-
$[2] === rotate ? t1 = $[3] : (t1 = { rotate }, $[2] = rotate, $[3] = t1);
|
|
1051
|
-
let t2;
|
|
1052
|
-
$[4] === progress ? t2 = $[5] : (t2 = SLICE_COLORS.map((color, index) => /* @__PURE__ */ jsx(Slice, {
|
|
1053
|
-
index,
|
|
1054
|
-
progress
|
|
1055
|
-
}, color)), $[4] = progress, $[5] = t2);
|
|
1056
|
-
let t3;
|
|
1057
|
-
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx("path", {
|
|
1058
|
-
d: "M9.43853 5.10896L11.1606 9.26642M13.8394 15.7336L15.5615 19.891M15.7336 11.1606L19.891 9.43853M9.26642 13.8394L5.10896 15.5615M5.3139 9.52342L9.23359 11.147M15.7664 13.853L19.6861 15.4766M13.853 9.23359L15.4766 5.3139M9.52342 19.6861L11.147 15.7664M20.5 12.5C20.5 16.9183 16.9183 20.5 12.5 20.5C8.08172 20.5 4.5 16.9183 4.5 12.5C4.5 8.08172 8.08172 4.5 12.5 4.5C16.9183 4.5 20.5 8.08172 20.5 12.5ZM16 12.5C16 14.433 14.433 16 12.5 16C10.567 16 9 14.433 9 12.5C9 10.567 10.567 9 12.5 9C14.433 9 16 10.567 16 12.5Z",
|
|
1059
|
-
stroke: "currentColor",
|
|
1060
|
-
strokeLinejoin: "round",
|
|
1061
|
-
strokeWidth: 1.2
|
|
1062
|
-
}), $[6] = t3) : t3 = $[6];
|
|
1063
|
-
let t4;
|
|
1064
|
-
return $[7] !== t1 || $[8] !== t2 ? (t4 = /* @__PURE__ */ jsxs(motion.svg, {
|
|
1065
|
-
"data-sanity-icon": "color-wheel",
|
|
1066
|
-
fill: "none",
|
|
1067
|
-
height: "1em",
|
|
1068
|
-
style: t1,
|
|
1069
|
-
viewBox: "0 0 25 25",
|
|
1070
|
-
width: "1em",
|
|
1071
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1072
|
-
children: [t2, t3]
|
|
1073
|
-
}), $[7] = t1, $[8] = t2, $[9] = t4) : t4 = $[9], t4;
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Starts loading the sidebar's code as the toggle is about to be pressed —
|
|
1077
|
-
* the same import `React.lazy` makes, which then waits for this one
|
|
1078
|
-
*/
|
|
1079
|
-
function preloadSidebar() {
|
|
1080
|
-
import("./ResizableSidebar-DD787kQr.js").catch(() => {});
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Opens or closes the sidebar — and notes the visit, which is what stops the
|
|
1084
|
-
* navbar from introducing the tool
|
|
1085
|
-
*/
|
|
1086
|
-
function toggleSidebar(send) {
|
|
1087
|
-
markVisited(), send({ type: "sidebar.toggle" });
|
|
1088
|
-
}
|
|
1089
|
-
/**
|
|
1090
|
-
* Whether the navbar has had its one chance per page load to introduce the
|
|
1091
|
-
* tool. Module state rather than component state on purpose: the button
|
|
1092
|
-
* remounts with the navbar (switching workspaces, crossing the narrow-screen
|
|
1093
|
-
* breakpoint) and renders once per Studio copy in the split preview, and none
|
|
1094
|
-
* of those should earn another introduction — only loading the page again does.
|
|
1095
|
-
*/
|
|
1096
|
-
let introduced = !1;
|
|
1097
|
-
/**
|
|
1098
|
-
* The topbar toggle, with an icon that plays its color wheel animation: on
|
|
1099
|
-
* hovering the button, every time; on hovering the navbar it sits in, once —
|
|
1100
|
-
* a "hey, look, new tool!" for anyone who has never opened the sidebar, which
|
|
1101
|
-
* the visit noted by {@link toggleSidebar} ends for good; and lap after lap
|
|
1102
|
-
* while the sidebar's code loads, the way a spinner would.
|
|
1103
|
-
*/
|
|
1104
|
-
function ThemerNavbarButton() {
|
|
1105
|
-
let $ = c(26), { open, loading, send } = useThemer(), progress = useMotionValue(0), [button, setButton] = useState(null), t0;
|
|
1106
|
-
$[0] === progress ? t0 = $[1] : (t0 = () => {
|
|
1107
|
-
progress.isAnimating() || animate(progress, [0, 1], {
|
|
1108
|
-
duration: ANIMATION_DURATION,
|
|
1109
|
-
ease: "linear"
|
|
1110
|
-
});
|
|
1111
|
-
}, $[0] = progress, $[1] = t0);
|
|
1112
|
-
let play = t0, t1, t2;
|
|
1113
|
-
$[2] !== loading || $[3] !== progress ? (t1 = () => {
|
|
1114
|
-
if (!loading) return;
|
|
1115
|
-
let looping = !0, lap = (from) => {
|
|
1116
|
-
animate(progress, [from, 1], {
|
|
1117
|
-
duration: (1 - from) * ANIMATION_DURATION,
|
|
1118
|
-
ease: "linear",
|
|
1119
|
-
onComplete: () => {
|
|
1120
|
-
looping && lap(0);
|
|
1121
|
-
}
|
|
1177
|
+
function ThemerThemeCrossfader(t0) {
|
|
1178
|
+
let $ = c(4), { actorRef, dispatch } = t0, t1, t2;
|
|
1179
|
+
return $[0] !== actorRef || $[1] !== dispatch ? (t1 = () => {
|
|
1180
|
+
let subscription = actorRef.subscribe((snapshot) => {
|
|
1181
|
+
let switching = snapshot.hasTag("switching"), theme = resolveActiveThemeOptions(snapshot.context);
|
|
1182
|
+
startTransition(() => {
|
|
1183
|
+
switching && addThemerTransitionType("crossfade"), dispatch(theme ? {
|
|
1184
|
+
type: "set-theme",
|
|
1185
|
+
theme
|
|
1186
|
+
} : { type: "unset-theme" });
|
|
1122
1187
|
});
|
|
1123
|
-
};
|
|
1124
|
-
return
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
let
|
|
1129
|
-
$[
|
|
1130
|
-
|
|
1131
|
-
$[
|
|
1132
|
-
let navbar = button?.closest(NAVBAR_SELECTOR);
|
|
1133
|
-
if (!navbar || introduced) return;
|
|
1134
|
-
let introduce = () => {
|
|
1135
|
-
introduced || (introduced = !0, hasVisited() || play());
|
|
1136
|
-
};
|
|
1137
|
-
return navbar.addEventListener("mouseenter", introduce, { once: !0 }), () => navbar.removeEventListener("mouseenter", introduce);
|
|
1138
|
-
}, t6 = [button, play], $[9] = button, $[10] = play, $[11] = t5, $[12] = t6) : (t5 = $[11], t6 = $[12]), useEffect(t5, t6);
|
|
1139
|
-
let t7;
|
|
1140
|
-
$[13] === Symbol.for("react.memo_cache_sentinel") ? (t7 = /* @__PURE__ */ jsx(Text, {
|
|
1141
|
-
size: 1,
|
|
1142
|
-
children: "Themer"
|
|
1143
|
-
}), $[13] = t7) : t7 = $[13];
|
|
1144
|
-
let t8;
|
|
1145
|
-
$[14] === progress ? t8 = $[15] : (t8 = /* @__PURE__ */ jsx(AnimatedColorWheelIcon, { progress }), $[14] = progress, $[15] = t8);
|
|
1146
|
-
let t9;
|
|
1147
|
-
$[16] === send ? t9 = $[17] : (t9 = () => toggleSidebar(send), $[16] = send, $[17] = t9);
|
|
1148
|
-
let t10;
|
|
1149
|
-
$[18] === play ? t10 = $[19] : (t10 = () => {
|
|
1150
|
-
preloadSidebar(), play();
|
|
1151
|
-
}, $[18] = play, $[19] = t10);
|
|
1152
|
-
let t11;
|
|
1153
|
-
return $[20] !== loading || $[21] !== open || $[22] !== t10 || $[23] !== t8 || $[24] !== t9 ? (t11 = /* @__PURE__ */ jsx(Tooltip, {
|
|
1154
|
-
animate: !0,
|
|
1155
|
-
content: t7,
|
|
1156
|
-
portal: !0,
|
|
1157
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
1158
|
-
"aria-busy": loading,
|
|
1159
|
-
"aria-label": "Themer",
|
|
1160
|
-
disabled: loading,
|
|
1161
|
-
icon: t8,
|
|
1162
|
-
mode: "bleed",
|
|
1163
|
-
onClick: t9,
|
|
1164
|
-
onFocus: preloadSidebar,
|
|
1165
|
-
onMouseEnter: t10,
|
|
1166
|
-
padding: 2,
|
|
1167
|
-
ref: setButton,
|
|
1168
|
-
selected: open
|
|
1169
|
-
})
|
|
1170
|
-
}), $[20] = loading, $[21] = open, $[22] = t10, $[23] = t8, $[24] = t9, $[25] = t11) : t11 = $[25], t11;
|
|
1188
|
+
});
|
|
1189
|
+
return () => subscription.unsubscribe();
|
|
1190
|
+
}, t2 = [actorRef, dispatch], $[0] = actorRef, $[1] = dispatch, $[2] = t1, $[3] = t2) : (t1 = $[2], t2 = $[3]), useEffect(t1, t2), null;
|
|
1191
|
+
}
|
|
1192
|
+
function RevokeImageUrls(t0) {
|
|
1193
|
+
let $ = c(3), { actorRef } = t0, t1, t2;
|
|
1194
|
+
return $[0] === actorRef ? (t1 = $[1], t2 = $[2]) : (t1 = () => () => {
|
|
1195
|
+
for (let url of Object.values(actorRef.getSnapshot().context.images)) URL.revokeObjectURL(url);
|
|
1196
|
+
}, t2 = [actorRef], $[0] = actorRef, $[1] = t1, $[2] = t2), useEffect(t1, t2), null;
|
|
1171
1197
|
}
|
|
1172
1198
|
/**
|
|
1173
|
-
*
|
|
1174
|
-
* navbar
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
* In the split preview every Studio copy renders its own navbar, so the
|
|
1178
|
-
* toggle shows up in both — they drive the same sidebar.
|
|
1179
|
-
*
|
|
1180
|
-
* @internal
|
|
1199
|
+
* Keep this component extremely lightweight, and only include state that are needed by both
|
|
1200
|
+
* `studio.components.layout` and `studio.components.navbar`, where it's not possible to prop-drill into `studio.components.navbar`,
|
|
1201
|
+
* and that need to survive open/close of the tool
|
|
1202
|
+
* @TODO will mount on the future `studio.components.provider` entrypoint to ensure other plugins using `studio.components.layout` is correctly wrapped in the top-level theme regardless of plugin ordering
|
|
1181
1203
|
*/
|
|
1182
|
-
function
|
|
1183
|
-
let $ = c(
|
|
1184
|
-
|
|
1185
|
-
let t1;
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1204
|
+
function ThemerProvider(t0) {
|
|
1205
|
+
let $ = c(18), { children, config } = t0;
|
|
1206
|
+
use(browser("The current theme is stored in localStorage."));
|
|
1207
|
+
let [persistedSnapshot] = useState(readPersistedSnapshot), t1;
|
|
1208
|
+
$[0] === persistedSnapshot ? t1 = $[1] : (t1 = (initialState) => {
|
|
1209
|
+
if (!persistedSnapshot) return initialState;
|
|
1210
|
+
let theme = resolveActiveThemeOptions(persistedSnapshot.context);
|
|
1211
|
+
return theme ? {
|
|
1212
|
+
...initialState,
|
|
1213
|
+
theme
|
|
1214
|
+
} : initialState;
|
|
1215
|
+
}, $[0] = persistedSnapshot, $[1] = t1);
|
|
1216
|
+
let [state, dispatch] = useReducer(toolReducer, initialToolState, t1), scheme = useColorSchemeValue(), index = useMediaIndex(), isMobile = index <= 2, isTooSmallForSplitScreen = index <= 3, open = state.open && !isMobile, split = state.split && !isTooSmallForSplitScreen, t2;
|
|
1217
|
+
$[2] === state.theme ? t2 = $[3] : (t2 = (state.theme === null ? null : buildTheme(state.theme)) ?? void 0, $[2] = state.theme, $[3] = t2);
|
|
1218
|
+
let t3 = state.theme?.[scheme]?.background, t4;
|
|
1219
|
+
$[4] !== children || $[5] !== config || $[6] !== open || $[7] !== persistedSnapshot || $[8] !== scheme || $[9] !== split || $[10] !== state.navbarHeight || $[11] !== state.prerender || $[12] !== state.prerenderSplitScreen || $[13] !== t3 ? (t4 = /* @__PURE__ */ jsx(ThemerLayout, {
|
|
1220
|
+
config,
|
|
1221
|
+
backgroundColor: t3,
|
|
1222
|
+
dispatch,
|
|
1223
|
+
navbarHeight: state.navbarHeight,
|
|
1224
|
+
open,
|
|
1225
|
+
persistedSnapshot,
|
|
1226
|
+
prerender: state.prerender,
|
|
1227
|
+
prerenderSplitScreen: state.prerenderSplitScreen,
|
|
1228
|
+
scheme,
|
|
1229
|
+
split,
|
|
1230
|
+
children
|
|
1231
|
+
}), $[4] = children, $[5] = config, $[6] = open, $[7] = persistedSnapshot, $[8] = scheme, $[9] = split, $[10] = state.navbarHeight, $[11] = state.prerender, $[12] = state.prerenderSplitScreen, $[13] = t3, $[14] = t4) : t4 = $[14];
|
|
1232
|
+
let t5;
|
|
1233
|
+
return $[15] !== t2 || $[16] !== t4 ? (t5 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
1234
|
+
theme: t2,
|
|
1235
|
+
children: t4
|
|
1236
|
+
}), $[15] = t2, $[16] = t4, $[17] = t5) : t5 = $[17], t5;
|
|
1209
1237
|
}
|
|
1210
1238
|
/**
|
|
1211
1239
|
* A Studio plugin that adds a themer sidebar for `buildTheme` themes: a
|
|
@@ -1236,22 +1264,27 @@ function ThemerNavbar(props) {
|
|
|
1236
1264
|
* @alpha
|
|
1237
1265
|
*/
|
|
1238
1266
|
const themerTool = definePlugin((options) => {
|
|
1239
|
-
let
|
|
1240
|
-
function ThemerLayoutWithOptions(props) {
|
|
1241
|
-
let $ = c(2), t0;
|
|
1242
|
-
return $[0] === props ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(ThemerLayout, {
|
|
1243
|
-
...props,
|
|
1244
|
-
baseOptions
|
|
1245
|
-
}), $[0] = props, $[1] = t0), t0;
|
|
1246
|
-
}
|
|
1267
|
+
let { title = "Themer" } = options ?? {};
|
|
1247
1268
|
return {
|
|
1248
1269
|
name: "@sanity/themer/tool",
|
|
1249
1270
|
studio: { components: {
|
|
1250
|
-
layout:
|
|
1271
|
+
layout: defineThemerLayout({ title }),
|
|
1251
1272
|
navbar: ThemerNavbar
|
|
1252
1273
|
} }
|
|
1253
1274
|
};
|
|
1254
1275
|
});
|
|
1276
|
+
function defineThemerLayout(config) {
|
|
1277
|
+
return function DefinedThemerLayoutProvider(props) {
|
|
1278
|
+
"use memo";
|
|
1279
|
+
let $ = c(4), t0;
|
|
1280
|
+
$[0] === props ? t0 = $[1] : (t0 = props.renderDefault(props), $[0] = props, $[1] = t0);
|
|
1281
|
+
let t1;
|
|
1282
|
+
return $[2] === t0 ? t1 = $[3] : (t1 = /* @__PURE__ */ jsx(ThemerProvider, {
|
|
1283
|
+
config,
|
|
1284
|
+
children: t0
|
|
1285
|
+
}), $[2] = t0, $[3] = t1), t1;
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1255
1288
|
export { themerTool };
|
|
1256
1289
|
|
|
1257
1290
|
//# sourceMappingURL=tool.js.map
|