@poveste/app 0.14.0 → 0.16.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/dist/app/style/main.pcss +18 -44
- package/dist/bundled/App.vue_vue_type_script_setup_true_lang.js +9 -7
- package/dist/bundled/app.css +91 -28
- package/dist/bundled/components/app/AppActions.vue_vue_type_script_setup_true_lang.js +44 -27
- package/dist/bundled/components/app/ShortcutTooltip.js +5 -0
- package/dist/bundled/components/app/ShortcutTooltip.vue_vue_type_script_setup_true_lang.js +27 -0
- package/dist/bundled/components/app/TopBarChipItem.js +1 -1
- package/dist/bundled/components/base/BaseDropdown.js +6 -0
- package/dist/bundled/components/base/BaseDropdown.vue_vue_type_script_setup_true_lang.js +68 -0
- package/dist/bundled/components/base/BaseEmpty.js +2 -2
- package/dist/bundled/components/base/BaseOverflowMenu.vue_vue_type_script_setup_true_lang.js +19 -19
- package/dist/bundled/components/base/BaseSelect.js +1 -0
- package/dist/bundled/components/base/BaseSelect.vue_vue_type_script_setup_true_lang.js +194 -33
- package/dist/bundled/components/base/BaseSplitPane.js +1 -1
- package/dist/bundled/components/base/BaseSplitPane.vue_vue_type_script_setup_true_lang.js +65 -4
- package/dist/bundled/components/command/PromptSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/panel/ControlsComponentPropItem.vue_vue_type_script_setup_true_lang.js +24 -16
- package/dist/bundled/components/panel/ControlsComponentProps.vue_vue_type_script_setup_true_lang.js +9 -6
- package/dist/bundled/components/panel/ControlsComponentState.vue_vue_type_script_setup_true_lang.js +9 -6
- package/dist/bundled/components/panel/PaneTabs.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/bundled/components/panel/StatePresets.vue_vue_type_script_setup_true_lang.js +96 -52
- package/dist/bundled/components/panel/StoryEvent.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/bundled/components/panel/StorySidePanel.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/bundled/components/panel/StorySourceCode.js +1 -1
- package/dist/bundled/components/panel/StorySourceCode.vue_vue_type_script_setup_true_lang.js +24 -18
- package/dist/bundled/components/search/SearchItemContent.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/bundled/components/search/SearchPane.vue_vue_type_script_setup_true_lang.js +1 -2
- package/dist/bundled/components/story/StoryResponsivePreview.js +1 -1
- package/dist/bundled/components/story/StoryResponsivePreview.vue_vue_type_script_setup_true_lang.js +52 -45
- package/dist/bundled/components/story/StoryVariantGridItem.js +1 -1
- package/dist/bundled/components/story/StoryVariantGridItem.vue_vue_type_script_setup_true_lang.js +22 -20
- package/dist/bundled/components/story/StoryVariantSingle.js +1 -1
- package/dist/bundled/components/story/StoryVariantSingle.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/bundled/components/story/StoryVariantSinglePreviewRemote.vue_vue_type_script_setup_true_lang.js +9 -6
- package/dist/bundled/components/story/StoryView.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/bundled/components/toolbar/DevOnlyToolbarOpenInEditor.vue_vue_type_script_setup_true_lang.js +15 -12
- package/dist/bundled/components/toolbar/ToolbarBackground.vue_vue_type_script_setup_true_lang.js +20 -16
- package/dist/bundled/components/toolbar/ToolbarNewTab.vue_vue_type_script_setup_true_lang.js +16 -13
- package/dist/bundled/components/toolbar/ToolbarResponsiveSize.vue_vue_type_script_setup_true_lang.js +35 -24
- package/dist/bundled/components/toolbar/ToolbarTextDirection.vue_vue_type_script_setup_true_lang.js +15 -12
- package/dist/bundled/components/toolbar/ToolbarTitle.js +1 -1
- package/dist/bundled/components/toolbar/ToolbarTitle.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/tree/StoryGroup.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/bundled/components/tree/StoryList.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/bundled/components/tree/StoryListFolder.js +1 -1
- package/dist/bundled/components/tree/StoryListFolder.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/bundled/components/tree/StoryListItem.js +1 -1
- package/dist/bundled/components/tree/StoryListItem.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/bundled/index.js +0 -13
- package/dist/bundled/sandbox.js +5 -3
- package/dist/bundled/stores/story.js +2 -2
- package/dist/bundled/util/commands.js +1 -1
- package/dist/bundled/util/dark.js +1 -1
- package/dist/bundled/util/events.js +1 -1
- package/dist/bundled/util/grid-slots.js +1 -1
- package/dist/bundled/util/icons.generated.js +4 -2
- package/dist/bundled/util/keyboard.js +8 -14
- package/dist/bundled/util/mapping.js +2 -2
- package/dist/bundled/util/render-tracker.js +33 -0
- package/dist/bundled/util/scroll.js +26 -5
- package/dist/bundled/util/state.js +105 -5
- package/dist/bundled/util/variant.js +12 -2
- package/dist/style.css +753 -357
- package/package.json +28 -28
- package/src/__tests__/base-select.spec.ts +25 -0
- package/src/__tests__/render-tracker.spec.ts +38 -0
- package/src/__tests__/reset.spec.ts +53 -0
- package/src/__tests__/state-clone.spec.ts +55 -1
- package/src/__tests__/state-types.spec.ts +175 -0
- package/src/app/App.vue +44 -35
- package/src/app/components/app/AppActions.vue +42 -30
- package/src/app/components/app/ShortcutTooltip.vue +29 -0
- package/src/app/components/app/TopBarChipItem.vue +1 -1
- package/src/app/components/base/BaseDropdown.vue +142 -0
- package/src/app/components/base/BaseEmpty.vue +1 -1
- package/src/app/components/base/BaseListItemLink.vue +4 -2
- package/src/app/components/base/BaseOverflowMenu.vue +27 -21
- package/src/app/components/base/BaseSelect.vue +270 -41
- package/src/app/components/base/BaseSplitPane.vue +75 -4
- package/src/app/components/command/PromptSelect.vue +1 -1
- package/src/app/components/panel/ControlsComponentPropItem.vue +29 -17
- package/src/app/components/panel/ControlsComponentProps.vue +7 -5
- package/src/app/components/panel/ControlsComponentState.vue +7 -5
- package/src/app/components/panel/PaneTabs.vue +6 -6
- package/src/app/components/panel/StatePresets.vue +116 -74
- package/src/app/components/panel/StoryEvent.vue +10 -9
- package/src/app/components/panel/StorySidePanel.vue +2 -1
- package/src/app/components/panel/StorySourceCode.vue +51 -44
- package/src/app/components/search/SearchItemContent.vue +2 -1
- package/src/app/components/search/SearchPane.vue +2 -3
- package/src/app/components/story/StoryResponsivePreview.vue +61 -29
- package/src/app/components/story/StoryVariantGridItem.vue +20 -19
- package/src/app/components/story/StoryVariantSingle.vue +1 -0
- package/src/app/components/story/StoryVariantSinglePreviewRemote.vue +14 -8
- package/src/app/components/story/StoryView.vue +4 -1
- package/src/app/components/toolbar/DevOnlyToolbarOpenInEditor.vue +14 -12
- package/src/app/components/toolbar/ToolbarBackground.vue +22 -23
- package/src/app/components/toolbar/ToolbarNewTab.vue +14 -12
- package/src/app/components/toolbar/ToolbarResponsiveSize.vue +31 -25
- package/src/app/components/toolbar/ToolbarTextDirection.vue +14 -10
- package/src/app/components/toolbar/ToolbarTitle.vue +2 -2
- package/src/app/components/tree/StoryGroup.vue +2 -2
- package/src/app/components/tree/StoryList.vue +2 -2
- package/src/app/components/tree/StoryListFolder.vue +2 -2
- package/src/app/components/tree/StoryListItem.vue +1 -1
- package/src/app/index.ts +0 -17
- package/src/app/sandbox.ts +16 -3
- package/src/app/stores/story.ts +2 -2
- package/src/app/style/main.pcss +18 -44
- package/src/app/types.ts +4 -4
- package/src/app/util/commands.ts +1 -1
- package/src/app/util/dark.ts +1 -1
- package/src/app/util/env.ts +0 -1
- package/src/app/util/events.ts +6 -5
- package/src/app/util/grid-slots.ts +1 -1
- package/src/app/util/icons.generated.ts +2 -2
- package/src/app/util/keyboard.ts +7 -11
- package/src/app/util/mapping.ts +2 -2
- package/src/app/util/render-tracker.ts +31 -0
- package/src/app/util/scroll.ts +47 -9
- package/src/app/util/state.ts +141 -8
- package/src/app/util/variant.ts +12 -2
- package/dist/app/style/floating.pcss +0 -40
- package/dist/bundled/util/tooltip.js +0 -14
- package/src/app/style/floating.pcss +0 -40
- package/src/app/util/tooltip.ts +0 -13
|
@@ -4,7 +4,7 @@ import { watch } from "@poveste/vendors/vue";
|
|
|
4
4
|
//#region src/app/util/dark.ts
|
|
5
5
|
var isDark = useDark({
|
|
6
6
|
valueDark: "ptw-dark",
|
|
7
|
-
initialValue: povesteConfig.theme.defaultColorScheme,
|
|
7
|
+
...povesteConfig.theme.defaultColorScheme ? { initialValue: povesteConfig.theme.defaultColorScheme } : {},
|
|
8
8
|
storageKey: "poveste-color-scheme",
|
|
9
9
|
storage: povesteConfig.theme.storeColorScheme ? localStorage : sessionStorage
|
|
10
10
|
});
|
|
@@ -19,7 +19,7 @@ async function logEvent(name, argument) {
|
|
|
19
19
|
}
|
|
20
20
|
function stringifyEvent(e) {
|
|
21
21
|
const obj = {};
|
|
22
|
-
for (const k in e) obj[k] = e
|
|
22
|
+
for (const k in e) obj[k] = Reflect.get(e, k);
|
|
23
23
|
return JSON.stringify(obj, (k, v) => {
|
|
24
24
|
if (v instanceof Node) return "Node";
|
|
25
25
|
if (v instanceof Window) return "Window";
|
|
@@ -6,11 +6,13 @@ var iconCollections = [
|
|
|
6
6
|
"add-alt": { "body": "<path fill=\"currentColor\" d=\"M16 4c6.6 0 12 5.4 12 12s-5.4 12-12 12S4 22.6 4 16S9.4 4 16 4m0-2C8.3 2 2 8.3 2 16s6.3 14 14 14s14-6.3 14-14S23.7 2 16 2\"/><path fill=\"currentColor\" d=\"M24 15h-7V8h-2v7H8v2h7v7h2v-7h7z\"/>" },
|
|
7
7
|
"area": { "body": "<path fill=\"currentColor\" d=\"M27 22.142V9.858A3.992 3.992 0 1 0 22.142 5H9.858A3.992 3.992 0 1 0 5 9.858v12.284A3.992 3.992 0 1 0 9.858 27h12.284A3.992 3.992 0 1 0 27 22.142M26 4a2 2 0 1 1-2 2a2 2 0 0 1 2-2M4 6a2 2 0 1 1 2 2a2 2 0 0 1-2-2m2 22a2 2 0 1 1 2-2a2 2 0 0 1-2 2m16.142-3H9.858A4 4 0 0 0 7 22.142V9.858A4 4 0 0 0 9.858 7h12.284A4 4 0 0 0 25 9.858v12.284A3.99 3.99 0 0 0 22.142 25M26 28a2 2 0 1 1 2-2a2.003 2.003 0 0 1-2 2\"/>" },
|
|
8
8
|
"audio-console": { "body": "<path fill=\"currentColor\" d=\"M28 14.005h-1v-12h-2v12h-1a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h1v10h2v-10h1a2.003 2.003 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 4h-4v-2h4Zm-10-12h-1v-4h-2v4h-1a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h1v18h2v-18h1a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 4h-4v-2h4Zm-10 10H7v-18H5v18H4a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h1v4h2v-4h1a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 4H4v-2h4Z\"/>" },
|
|
9
|
+
"calendar": { "body": "<path fill=\"currentColor\" d=\"M26 4h-4V2h-2v2h-8V2h-2v2H6c-1.1 0-2 .9-2 2v20c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2m0 22H6V12h20zm0-16H6V6h4v2h2V6h8v2h2V6h4z\"/>" },
|
|
9
10
|
"caret-down": { "body": "<path fill=\"currentColor\" d=\"m24 12l-8 10l-8-10z\"/>" },
|
|
10
11
|
"caret-right": { "body": "<path fill=\"currentColor\" d=\"m12 8l10 8l-10 8z\"/>" },
|
|
11
12
|
"checkbox": { "body": "<path fill=\"currentColor\" d=\"M26 4H6a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2M6 26V6h20v20Z\"/>" },
|
|
12
13
|
"checkmark": { "body": "<path fill=\"currentColor\" d=\"m13 24l-9-9l1.414-1.414L13 21.171L26.586 7.586L28 9z\"/>" },
|
|
13
14
|
"chevron-down": { "body": "<path fill=\"currentColor\" d=\"M16 22L6 12l1.4-1.4l8.6 8.6l8.6-8.6L26 12z\"/>" },
|
|
15
|
+
"chevron-left": { "body": "<path fill=\"currentColor\" d=\"M10 16L20 6l1.4 1.4l-8.6 8.6l8.6 8.6L20 26z\"/>" },
|
|
14
16
|
"chevron-right": { "body": "<path fill=\"currentColor\" d=\"M22 16L12 26l-1.4-1.4l8.6-8.6l-8.6-8.6L12 6z\"/>" },
|
|
15
17
|
"chevron-sort": { "body": "<path fill=\"currentColor\" d=\"m16 28l-7-7l1.41-1.41L16 25.17l5.59-5.58L23 21zm0-24l7 7l-1.41 1.41L16 6.83l-5.59 5.58L9 11z\"/>" },
|
|
16
18
|
"close": { "body": "<path fill=\"currentColor\" d=\"M17.414 16L24 9.414L22.586 8L16 14.586L9.414 8L8 9.414L14.586 16L8 22.586L9.414 24L16 17.414L22.586 24L24 22.586z\"/>" },
|
|
@@ -54,7 +56,7 @@ var iconCollections = [
|
|
|
54
56
|
"view-off": { "body": "<path fill=\"currentColor\" d=\"m5.24 22.51l1.43-1.42A14.06 14.06 0 0 1 3.07 16C5.1 10.93 10.7 7 16 7a12.4 12.4 0 0 1 4 .72l1.55-1.56A14.7 14.7 0 0 0 16 5A16.69 16.69 0 0 0 1.06 15.66a1 1 0 0 0 0 .68a16 16 0 0 0 4.18 6.17\"/><path fill=\"currentColor\" d=\"M12 15.73a4 4 0 0 1 3.7-3.7l1.81-1.82a6 6 0 0 0-7.33 7.33Zm18.94-.07a16.4 16.4 0 0 0-5.74-7.44L30 3.41L28.59 2L2 28.59L3.41 30l5.1-5.1A15.3 15.3 0 0 0 16 27a16.69 16.69 0 0 0 14.94-10.66a1 1 0 0 0 0-.68M20 16a4 4 0 0 1-6 3.44L19.44 14a4 4 0 0 1 .56 2m-4 9a13.05 13.05 0 0 1-6-1.58l2.54-2.54a6 6 0 0 0 8.35-8.35l2.87-2.87A14.54 14.54 0 0 1 28.93 16C26.9 21.07 21.3 25 16 25\"/>" },
|
|
55
57
|
"warning-alt": { "body": "<path fill=\"currentColor\" d=\"M16 23a1.5 1.5 0 1 0 1.5 1.5A1.5 1.5 0 0 0 16 23m-1-11h2v9h-2z\"/><path fill=\"currentColor\" d=\"M29 30H3a1 1 0 0 1-.887-1.461l13-25a1 1 0 0 1 1.774 0l13 25A1 1 0 0 1 29 30M4.65 28h22.7l.001-.003L16.002 6.17h-.004L4.648 27.997Z\"/>" }
|
|
56
58
|
},
|
|
57
|
-
"lastModified":
|
|
59
|
+
"lastModified": 1789034045,
|
|
58
60
|
"aliases": { "software-resource-resource": { "parent": "software-resource-reference" } },
|
|
59
61
|
"width": 32,
|
|
60
62
|
"height": 32
|
|
@@ -73,7 +75,7 @@ var iconCollections = [
|
|
|
73
75
|
"height": 16
|
|
74
76
|
}
|
|
75
77
|
},
|
|
76
|
-
"lastModified":
|
|
78
|
+
"lastModified": 1789279072,
|
|
77
79
|
"width": 20,
|
|
78
80
|
"height": 20
|
|
79
81
|
},
|
|
@@ -24,30 +24,24 @@ var pressedKeys = /* @__PURE__ */ new Set();
|
|
|
24
24
|
var trackedWindows = shallowRef([]);
|
|
25
25
|
function bindTracking(target) {
|
|
26
26
|
const onKeydown = (event) => {
|
|
27
|
-
for (const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
mod.pressed = true;
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
27
|
+
for (const mod of Object.values(modifiers)) if (mod.key === event.key) {
|
|
28
|
+
mod.pressed = true;
|
|
29
|
+
return;
|
|
33
30
|
}
|
|
34
31
|
pressedKeys.add(event.key.toLocaleLowerCase());
|
|
35
32
|
};
|
|
36
33
|
const onKeyup = (event) => {
|
|
37
34
|
requestAnimationFrame(() => {
|
|
38
35
|
pressedKeys.clear();
|
|
39
|
-
for (const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
mod.pressed = false;
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
36
|
+
for (const mod of Object.values(modifiers)) if (mod.key === event.key) {
|
|
37
|
+
mod.pressed = false;
|
|
38
|
+
break;
|
|
45
39
|
}
|
|
46
40
|
});
|
|
47
41
|
};
|
|
48
42
|
const onBlur = () => {
|
|
49
43
|
pressedKeys.clear();
|
|
50
|
-
for (const
|
|
44
|
+
for (const mod of Object.values(modifiers)) mod.pressed = false;
|
|
51
45
|
};
|
|
52
46
|
target.addEventListener("keydown", onKeydown);
|
|
53
47
|
target.addEventListener("keyup", onKeyup);
|
|
@@ -89,7 +83,7 @@ function isMatchingCombination(combination) {
|
|
|
89
83
|
const splitted = combination.split("+").map((key) => key.trim());
|
|
90
84
|
const targetKey = splitted.pop();
|
|
91
85
|
if (!targetKey) return false;
|
|
92
|
-
for (const mod
|
|
86
|
+
for (const [name, mod] of Object.entries(modifiers)) if (splitted.includes(name) !== mod.pressed) return false;
|
|
93
87
|
return pressedKeys.has(targetKey);
|
|
94
88
|
}
|
|
95
89
|
function formatKey(key) {
|
|
@@ -20,7 +20,7 @@ function mapFile(file, existingFile) {
|
|
|
20
20
|
file: markRaw(result),
|
|
21
21
|
variants: file.story.variants.map((v) => mapVariant(v, existingFile.story.variants.find((item) => item.id === v.id)))
|
|
22
22
|
};
|
|
23
|
-
else if (key !== "component") result
|
|
23
|
+
else if (key !== "component") Reflect.set(result, key, Reflect.get(file, key));
|
|
24
24
|
} else result = {
|
|
25
25
|
...file,
|
|
26
26
|
component: markRaw(file.component),
|
|
@@ -38,7 +38,7 @@ function mapVariant(variant, existingVariant) {
|
|
|
38
38
|
let result;
|
|
39
39
|
if (existingVariant) {
|
|
40
40
|
result = existingVariant;
|
|
41
|
-
for (const key in variant) if (!copiedFromExistingVariant.includes(key)) result
|
|
41
|
+
for (const key in variant) if (!copiedFromExistingVariant.includes(key)) Reflect.set(result, key, Reflect.get(variant, key));
|
|
42
42
|
} else result = {
|
|
43
43
|
...variant,
|
|
44
44
|
state: reactive({
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/app/util/render-tracker.ts
|
|
2
|
+
/**
|
|
3
|
+
* Whether the render now under way inside a realm reported a throw.
|
|
4
|
+
*
|
|
5
|
+
* The host cannot see inside the realm, so "did this render succeed" has to be
|
|
6
|
+
* read off the two named messages it does see. A throw reaches
|
|
7
|
+
* `app.config.errorHandler` during mount and `SANDBOX_READY` is emitted after
|
|
8
|
+
* `await host.mount()` returns, so the order is always error-then-ready — which
|
|
9
|
+
* makes a ready with nothing recorded since the render began a clean render.
|
|
10
|
+
*
|
|
11
|
+
* Clearing on the story and variant ids changing instead cleared the pair being
|
|
12
|
+
* navigated *to*, so arriving at a broken story wiped its marker, and the fixed
|
|
13
|
+
* story the comment was about was never reached at all: a file edited in place
|
|
14
|
+
* keeps its ids, so that watcher did not fire on the one path it named (#597).
|
|
15
|
+
*/
|
|
16
|
+
function createRenderTracker() {
|
|
17
|
+
let threw = false;
|
|
18
|
+
return {
|
|
19
|
+
/** A new render is starting: what the previous one reported no longer holds. */
|
|
20
|
+
begin() {
|
|
21
|
+
threw = false;
|
|
22
|
+
},
|
|
23
|
+
threw() {
|
|
24
|
+
threw = true;
|
|
25
|
+
},
|
|
26
|
+
/** True when the render that has just reported ready said nothing before it. */
|
|
27
|
+
wasClean() {
|
|
28
|
+
return !threw;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { createRenderTracker };
|
|
@@ -1,17 +1,38 @@
|
|
|
1
1
|
import { onMounted, watch } from "@poveste/vendors/vue";
|
|
2
|
-
import scrollIntoView from "@poveste/vendors/scroll";
|
|
3
2
|
//#region src/app/util/scroll.ts
|
|
3
|
+
/**
|
|
4
|
+
* The closest ancestor that can actually scroll, or `null` for the viewport.
|
|
5
|
+
*
|
|
6
|
+
* `overlay` is in the test because it is what `overflow: overlay` computes to
|
|
7
|
+
* where it is still supported, and an element scrolling under it would
|
|
8
|
+
* otherwise be measured against the wrong box.
|
|
9
|
+
*/
|
|
10
|
+
function scrollParent(el) {
|
|
11
|
+
for (let node = el.parentElement; node; node = node.parentElement) {
|
|
12
|
+
const style = getComputedStyle(node);
|
|
13
|
+
if (/auto|scroll|overlay/.test(style.overflowY + style.overflowX)) return node;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
/** Whether the element is already wholly inside the box that would scroll. */
|
|
18
|
+
function isVisibleIn(el, scroller) {
|
|
19
|
+
const rect = el.getBoundingClientRect();
|
|
20
|
+
const bounds = scroller?.getBoundingClientRect();
|
|
21
|
+
const top = bounds?.top ?? 0;
|
|
22
|
+
const bottom = bounds?.bottom ?? window.innerHeight;
|
|
23
|
+
return rect.top >= top && rect.bottom <= bottom;
|
|
24
|
+
}
|
|
4
25
|
function useScrollOnActive(active, el) {
|
|
5
26
|
watch(active, (value) => {
|
|
6
27
|
if (value) autoScroll();
|
|
7
28
|
}, { flush: "post" });
|
|
8
29
|
function autoScroll() {
|
|
9
|
-
|
|
10
|
-
|
|
30
|
+
const target = el.value;
|
|
31
|
+
if (!target || isVisibleIn(target, scrollParent(target))) return;
|
|
32
|
+
target.scrollIntoView({
|
|
11
33
|
block: "center",
|
|
12
34
|
inline: "nearest",
|
|
13
|
-
behavior: "smooth"
|
|
14
|
-
boundary: document.body
|
|
35
|
+
behavior: "smooth"
|
|
15
36
|
});
|
|
16
37
|
}
|
|
17
38
|
onMounted(() => {
|
|
@@ -1,17 +1,48 @@
|
|
|
1
1
|
import { isRef, unref } from "@poveste/vendors/vue";
|
|
2
|
-
import { clone, omit } from "@poveste/shared";
|
|
2
|
+
import { clone, isMarkedRaw, isPlainObject, omit } from "@poveste/shared";
|
|
3
3
|
//#region src/app/util/state.ts
|
|
4
4
|
var isObject = (val) => val !== null && typeof val === "object";
|
|
5
|
+
/**
|
|
6
|
+
* A value the caller must leave out rather than carry, because it cannot make
|
|
7
|
+
* the trip this copy is being made for.
|
|
8
|
+
*/
|
|
9
|
+
var DROP = Symbol("poveste.drop");
|
|
10
|
+
/**
|
|
11
|
+
* A detached copy of `val`, with refs unwrapped.
|
|
12
|
+
*
|
|
13
|
+
* `clean` means the copy leaves the realm — it is posted to the other side of
|
|
14
|
+
* the sandbox bridge, or stored as a preset — so values that cannot survive
|
|
15
|
+
* that are dropped rather than carried: functions, and anything Vue has marked
|
|
16
|
+
* raw.
|
|
17
|
+
*
|
|
18
|
+
* Marked values are dropped rather than passed through, because `__v_skip` is
|
|
19
|
+
* non-enumerable and does not cross `structuredClone`. Handing one over would
|
|
20
|
+
* strip the mark and hand the far side a plain graph to deep-watch, which is
|
|
21
|
+
* how the first attempt at #957 turned a slow story into a hanging one. A key
|
|
22
|
+
* the sandbox never sends is a key the controls panel does not show, and for
|
|
23
|
+
* the case this aims at — a template ref onto a component's exposed object —
|
|
24
|
+
* that is a control nobody could have used anyway.
|
|
25
|
+
*
|
|
26
|
+
* Without `clean` the copy stays in this realm, so a marked value is carried by
|
|
27
|
+
* reference. That is what the two Vues' bridge wants: an identity both sides
|
|
28
|
+
* share, which `isEquivalent` then settles by `Object.is` instead of walking.
|
|
29
|
+
*/
|
|
5
30
|
function toRawDeep(val, clean = false, seen = /* @__PURE__ */ new WeakMap()) {
|
|
31
|
+
const value = walk(val, clean, seen);
|
|
32
|
+
return value === DROP ? {} : value;
|
|
33
|
+
}
|
|
34
|
+
function walk(val, clean, seen) {
|
|
6
35
|
const unwrappedValue = isRef(val) ? unref(val) : val;
|
|
7
36
|
if (typeof unwrappedValue === "symbol") return unwrappedValue.toString();
|
|
8
37
|
if (!isObject(unwrappedValue)) return unwrappedValue;
|
|
38
|
+
if (isMarkedRaw(unwrappedValue)) return clean ? DROP : unwrappedValue;
|
|
9
39
|
if (seen.has(unwrappedValue)) return seen.get(unwrappedValue);
|
|
40
|
+
if (!Array.isArray(unwrappedValue) && !isPlainObject(unwrappedValue)) return notPlain(unwrappedValue, clean, seen);
|
|
10
41
|
if (Array.isArray(unwrappedValue)) {
|
|
11
42
|
const result = [];
|
|
12
43
|
seen.set(unwrappedValue, result);
|
|
13
|
-
let list = unwrappedValue.map((value) =>
|
|
14
|
-
if (clean) list = list.filter((value) => typeof value !== "function");
|
|
44
|
+
let list = unwrappedValue.map((value) => walk(value, clean, seen));
|
|
45
|
+
if (clean) list = list.filter((value) => typeof value !== "function" && value !== DROP);
|
|
15
46
|
result.push(...list);
|
|
16
47
|
return result;
|
|
17
48
|
} else {
|
|
@@ -21,10 +52,79 @@ function toRawDeep(val, clean = false, seen = /* @__PURE__ */ new WeakMap()) {
|
|
|
21
52
|
return result;
|
|
22
53
|
}
|
|
23
54
|
}
|
|
24
|
-
|
|
55
|
+
/**
|
|
56
|
+
* A value with a prototype of its own — a `Date`, a `Map`, a class instance.
|
|
57
|
+
*
|
|
58
|
+
* These used to fall past every branch into `toRawObject`, whose `Object.keys`
|
|
59
|
+
* is empty for most of them, so they arrived as `{}` with no error and no
|
|
60
|
+
* warning (#977). `postMessage` is structured clone, and structured clone
|
|
61
|
+
* carries `Date`, `Map`, `Set`, `RegExp`, `Error`, typed arrays and `BigInt`
|
|
62
|
+
* intact — so they were being destroyed here, before the transport that would
|
|
63
|
+
* have carried them was ever reached.
|
|
64
|
+
*
|
|
65
|
+
* Structured clone is therefore the line, rather than a list authored here:
|
|
66
|
+
* it is the boundary that actually exists, it needs no maintaining as the
|
|
67
|
+
* platform adds types, and adopting it is what makes this walk agree with
|
|
68
|
+
* `isEquivalent`, which refuses to compare anything that is not plain.
|
|
69
|
+
*
|
|
70
|
+
* Without `clean` the copy stays in this realm, so the value is carried by
|
|
71
|
+
* reference — the same answer `copyState` in `@poveste/shared` gives, and the
|
|
72
|
+
* identity `isEquivalent` then settles by `Object.is`.
|
|
73
|
+
*/
|
|
74
|
+
function notPlain(value, clean, seen) {
|
|
75
|
+
if (!clean) return value;
|
|
76
|
+
if (value instanceof Map || value instanceof Set) return walkCollection(value, seen);
|
|
77
|
+
try {
|
|
78
|
+
const copy = structuredClone(value);
|
|
79
|
+
seen.set(value, copy);
|
|
80
|
+
return copy;
|
|
81
|
+
} catch {
|
|
82
|
+
return DROP;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `Map` and `Set`, walked rather than handed to `structuredClone`, for two
|
|
87
|
+
* measured reasons.
|
|
88
|
+
*
|
|
89
|
+
* They hold user values, so this walk's own rules have to reach inside them: a
|
|
90
|
+
* function or a marked value in a `Map` is the same problem it is in an array,
|
|
91
|
+
* and an array already comes out shorter for it. Cloning the whole container
|
|
92
|
+
* would carry a function straight into a `DataCloneError`, and would strip a
|
|
93
|
+
* marked value's `__v_skip` and hand the far side a plain graph to deep-watch
|
|
94
|
+
* — which is the hazard #974 exists to prevent.
|
|
95
|
+
*
|
|
96
|
+
* And these are the only types here that Vue proxies. `structuredClone` throws
|
|
97
|
+
* `DataCloneError` on a reactive `Map` while the raw one clones fine, so a
|
|
98
|
+
* `Map` in reactive state would be refused and dropped — a second silence
|
|
99
|
+
* rather than a fix for the first. `Date`, `RegExp`, `Error` and typed arrays
|
|
100
|
+
* are not proxied, so the path below never meets a proxy and needs no `toRaw`.
|
|
101
|
+
*/
|
|
102
|
+
function walkCollection(value, seen) {
|
|
103
|
+
const keep = (walked) => walked !== DROP && typeof walked !== "function";
|
|
104
|
+
if (value instanceof Set) {
|
|
105
|
+
const copy = /* @__PURE__ */ new Set();
|
|
106
|
+
seen.set(value, copy);
|
|
107
|
+
for (const entry of value) {
|
|
108
|
+
const walked = walk(entry, true, seen);
|
|
109
|
+
if (keep(walked)) copy.add(walked);
|
|
110
|
+
}
|
|
111
|
+
return copy;
|
|
112
|
+
}
|
|
113
|
+
const copy = /* @__PURE__ */ new Map();
|
|
114
|
+
seen.set(value, copy);
|
|
115
|
+
for (const [key, entry] of value) {
|
|
116
|
+
const walkedKey = walk(key, true, seen);
|
|
117
|
+
const walked = walk(entry, true, seen);
|
|
118
|
+
if (keep(walkedKey) && keep(walked)) copy.set(walkedKey, walked);
|
|
119
|
+
}
|
|
120
|
+
return copy;
|
|
121
|
+
}
|
|
122
|
+
function toRawObject(obj, target, clean, seen) {
|
|
25
123
|
Object.keys(obj).forEach((key) => {
|
|
26
124
|
if (clean && typeof obj[key] === "function") return;
|
|
27
|
-
|
|
125
|
+
const value = walk(obj[key], clean, seen);
|
|
126
|
+
if (value === DROP) return;
|
|
127
|
+
target[key] = value;
|
|
28
128
|
});
|
|
29
129
|
}
|
|
30
130
|
/**
|
|
@@ -8,14 +8,24 @@ import { useRoute } from "@poveste/vendors/vue-router";
|
|
|
8
8
|
* has to tell "a variant is on its way" from "this story is waiting for the
|
|
9
9
|
* reader to pick one" needs the same answer it will give — hence one predicate
|
|
10
10
|
* rather than two copies of the condition (#328).
|
|
11
|
+
*
|
|
12
|
+
* Any story with variants, deliberately. It used to require a remembered choice
|
|
13
|
+
* or exactly one variant, which made `StoryView`'s `variants[0]` fallback
|
|
14
|
+
* unreachable: a story with several variants and no history satisfied neither
|
|
15
|
+
* clause, so the first visit of a session showed an empty panel and every visit
|
|
16
|
+
* after it worked, because the first one recorded the history (#724).
|
|
17
|
+
*
|
|
18
|
+
* No layout clause. Grid stories already auto-selected on revisit through the
|
|
19
|
+
* remembered choice, so excluding them here would auto-open them on the second
|
|
20
|
+
* visit and never the first — this defect, made permanent for one layout.
|
|
11
21
|
*/
|
|
12
22
|
function autoSelectsVariant(story) {
|
|
13
|
-
return !!story &&
|
|
23
|
+
return !!story && story.variants.length > 0;
|
|
14
24
|
}
|
|
15
25
|
function useCurrentVariantRoute(variant) {
|
|
16
26
|
const route = useRoute();
|
|
17
27
|
return {
|
|
18
|
-
isActive: computed(() => route.query
|
|
28
|
+
isActive: computed(() => route.query["variantId"] === variant.value.id),
|
|
19
29
|
targetRoute: computed(() => ({
|
|
20
30
|
...route,
|
|
21
31
|
query: {
|