@poveste/app 0.20.0 → 0.21.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.
Files changed (39) hide show
  1. package/dist/bundled/app.css +55 -27
  2. package/dist/bundled/components/layout/LayoutModal.vue_vue_type_script_setup_true_lang.js +44 -5
  3. package/dist/bundled/components/panel/ComponentDocs.js +1 -1
  4. package/dist/bundled/components/panel/ComponentDocs.vue_vue_type_script_setup_true_lang.js +72 -40
  5. package/dist/bundled/components/panel/StoryDocs.vue_vue_type_script_setup_true_lang.js +34 -10
  6. package/dist/bundled/components/panel/StorySourceCode.js +1 -1
  7. package/dist/bundled/components/panel/StorySourceCode.vue_vue_type_script_setup_true_lang.js +2 -2
  8. package/dist/bundled/components/story/StoryResponsivePreview.js +1 -1
  9. package/dist/bundled/components/story/StoryResponsivePreview.vue_vue_type_script_setup_true_lang.js +5 -3
  10. package/dist/bundled/components/story/StoryVariantGridItem.js +1 -1
  11. package/dist/bundled/components/story/StoryVariantGridItem.vue_vue_type_script_setup_true_lang.js +5 -3
  12. package/dist/bundled/components/story/StoryVariantSinglePreviewRemote.vue_vue_type_script_setup_true_lang.js +3 -2
  13. package/dist/bundled/index.js +5 -1
  14. package/dist/bundled/sandbox.js +2 -0
  15. package/dist/bundled/util/component-docs-data.js +5 -1
  16. package/dist/bundled/util/component-docs.js +90 -14
  17. package/dist/bundled/util/dark.js +3 -1
  18. package/dist/bundled/util/docs.js +12 -2
  19. package/dist/bundled/util/highlighter.js +1 -1
  20. package/dist/bundled/util/icons.generated.js +1 -0
  21. package/dist/bundled/util/keyboard.js +5 -4
  22. package/dist/style.css +317 -150
  23. package/package.json +7 -7
  24. package/src/app/components/layout/LayoutModal.vue +55 -2
  25. package/src/app/components/panel/ComponentDocs.vue +83 -17
  26. package/src/app/components/panel/StoryDocs.vue +30 -3
  27. package/src/app/components/panel/StorySourceCode.vue +2 -2
  28. package/src/app/components/story/StoryResponsivePreview.vue +4 -1
  29. package/src/app/components/story/StoryVariantGridItem.vue +4 -1
  30. package/src/app/components/story/StoryVariantSinglePreviewRemote.vue +6 -4
  31. package/src/app/index.ts +12 -3
  32. package/src/app/sandbox.ts +5 -0
  33. package/src/app/util/component-docs-data.ts +6 -3
  34. package/src/app/util/component-docs.ts +102 -11
  35. package/src/app/util/dark.ts +6 -0
  36. package/src/app/util/docs.ts +18 -2
  37. package/src/app/util/highlighter.ts +2 -1
  38. package/src/app/util/icons.generated.ts +1 -1
  39. package/src/app/util/keyboard.ts +8 -4
@@ -35,8 +35,8 @@ var StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
35
35
  emits: { resize: (_width, _height) => true },
36
36
  setup(__props, { emit: __emit }) {
37
37
  useCssVars((_ctx) => ({
38
- "v2ce2d009": unref(variant).iconColor,
39
- "v4f3f7242": unref(settings).backgroundColor
38
+ "ff0ecc2c": unref(variant).iconColor,
39
+ "v59a40d7e": unref(settings).backgroundColor
40
40
  }));
41
41
  const props = __props;
42
42
  const emit = __emit;
@@ -104,7 +104,9 @@ var StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
104
104
  variant: unref(variant),
105
105
  story: __props.story
106
106
  }, null, 8, ["variant", "story"])])]), createElementVNode("div", {
107
- class: normalizeClass(["border bg-white dark:bg-gray-700 rounded flex-1 p-4 relative", {
107
+ class: normalizeClass(["border rounded flex-1 p-4 relative", {
108
+ "bg-gray-700": unref(previewDark),
109
+ "bg-white": !unref(previewDark),
108
110
  "border-gray-100 dark:border-gray-800": !unref(isActive),
109
111
  "border-primary-200 dark:border-primary-900": unref(isActive)
110
112
  }]),
@@ -59,7 +59,7 @@ var StoryVariantSinglePreviewRemote_vue_vue_type_script_setup_true_lang_default
59
59
  iframe.value?.contentWindow?.postMessage({
60
60
  type: STATE_SYNC,
61
61
  state: changes
62
- });
62
+ }, window.location.origin);
63
63
  }
64
64
  let bridge = createStateBridge(postState);
65
65
  function syncState() {
@@ -79,6 +79,7 @@ var StoryVariantSinglePreviewRemote_vue_vue_type_script_setup_true_lang_default
79
79
  }
80
80
  useEventListener(window, "message", (event) => {
81
81
  if (event.source !== iframe.value?.contentWindow) return;
82
+ if (event.origin !== window.location.origin) return;
82
83
  switch (event.data?.type) {
83
84
  case STATE_SYNC:
84
85
  if (!fromCurrentOccupant(event.data)) break;
@@ -193,7 +194,7 @@ var StoryVariantSinglePreviewRemote_vue_vue_type_script_setup_true_lang_default
193
194
  if (iframe.value?.contentWindow) iframe.value.contentWindow.postMessage({
194
195
  type: PREVIEW_SETTINGS_SYNC,
195
196
  settings: toRaw(settings)
196
- });
197
+ }, window.location.origin);
197
198
  }
198
199
  watch(() => settings, () => {
199
200
  syncSettings();
@@ -7,11 +7,15 @@ import { iconCollections } from "./util/icons.generated.js";
7
7
  import { createApp, watch } from "@poveste/vendors/vue";
8
8
  import { addCollection } from "@poveste/vendors/iconify";
9
9
  import { createPinia } from "@poveste/vendors/pinia";
10
- import { generation } from "virtual:$poveste-stories";
10
+ import { generation, icons, onUpdate } from "virtual:$poveste-stories";
11
11
  import "virtual:$poveste-theme";
12
12
  //#region src/app/index.ts
13
13
  async function mountMainApp() {
14
14
  for (const collection of iconCollections) addCollection(collection);
15
+ for (const collection of icons) addCollection(collection);
16
+ onUpdate((_files, _tree, icons = []) => {
17
+ for (const collection of icons) addCollection(collection);
18
+ });
15
19
  watch(isDark, (dark) => {
16
20
  document.documentElement.style.colorScheme = dark ? "dark" : "light";
17
21
  }, { immediate: true });
@@ -17,6 +17,7 @@ import { createPinia } from "@poveste/vendors/pinia";
17
17
  import { files, onUpdate } from "virtual:$poveste-stories";
18
18
  import { parseQuery } from "@poveste/vendors/vue-router";
19
19
  import { reportStoryError } from "@poveste/shared";
20
+ import "virtual:$poveste-theme";
20
21
  //#region src/app/sandbox.ts
21
22
  var query = parseQuery(window.location.search);
22
23
  var storyId = ref(typeof query["storyId"] === "string" ? query["storyId"] : null);
@@ -56,6 +57,7 @@ watch(previewDark, (value) => {
56
57
  if (!el) continue;
57
58
  for (const className of previewDarkClasses()) el.classList.toggle(className, value);
58
59
  }
60
+ document.documentElement.classList.toggle("ptw-dark", value);
59
61
  }, { immediate: true });
60
62
  var reportedColorScheme = "";
61
63
  function reportColorScheme() {
@@ -1 +1,5 @@
1
- export { componentDocs } from "virtual:$poveste-component-docs";
1
+ import { componentDocs as componentDocs$1 } from "virtual:$poveste-component-docs";
2
+ //#region src/app/util/component-docs-data.ts
3
+ var componentDocs = componentDocs$1;
4
+ //#endregion
5
+ export { componentDocs };
@@ -1,13 +1,57 @@
1
1
  import { onBeforeUnmount, ref, watch } from "@poveste/vendors/vue";
2
2
  //#region src/app/util/component-docs.ts
3
3
  function toEntries(result) {
4
- return Object.entries(result.components).map(([file, { doc, error }]) => ({
4
+ return Object.entries(result.components).map(([file, { doc, error, name }]) => ({
5
5
  file,
6
- name: file.split("/").pop().replace(/\.[^.]+$/, ""),
6
+ name: doc?.name ?? name ?? file.split("/").pop().replace(/(?:\.story)?\.[^.]+$/, ""),
7
7
  ...doc ? { doc } : {},
8
8
  ...error ? { error } : {}
9
9
  }));
10
10
  }
11
+ /**
12
+ * Only the sections a story's `autoDocs` names, in the table's own order. A component
13
+ * left with nothing to show is dropped; one that failed keeps its error (#1398).
14
+ */
15
+ function onlySections(entries, autoDocs) {
16
+ if (!Array.isArray(autoDocs)) return entries;
17
+ return entries.flatMap((entry) => {
18
+ if (!entry.doc) return [entry];
19
+ const doc = {
20
+ ...entry.doc,
21
+ props: autoDocs.includes("props") ? entry.doc.props : [],
22
+ slots: autoDocs.includes("slots") ? entry.doc.slots : [],
23
+ events: autoDocs.includes("events") ? entry.doc.events : []
24
+ };
25
+ return doc.props.length || doc.slots.length || doc.events.length ? [{
26
+ ...entry,
27
+ doc
28
+ }] : [];
29
+ });
30
+ }
31
+ /**
32
+ * A compound component's root first, `SelectRoot` before `SelectTrigger`, else the order
33
+ * they came in. A story rendering two families, such as a Select inside a Calendar, opens
34
+ * the root its own folder or file is named for: `Calendar/story/CalendarSelect.story.vue`
35
+ * opens `CalendarRoot`, not the `SelectRoot` it imported first.
36
+ */
37
+ function rootFirst(entries, storyFile) {
38
+ const roots = entries.filter((entry) => entry.name.endsWith("Root"));
39
+ const family = (entry) => entry.name.slice(0, -4);
40
+ const segments = storyFile?.split(/[\\/]/) ?? [];
41
+ const fileName = segments.at(-1) ?? "";
42
+ const root = roots.find((entry) => segments.slice(0, -1).includes(family(entry))) ?? roots.find((entry) => fileName.startsWith(family(entry))) ?? roots[0];
43
+ return root ? [root, ...entries.filter((entry) => entry !== root)] : entries;
44
+ }
45
+ /**
46
+ * The `v-model` a prop and its `update:` event make together, as a template writes it:
47
+ * `v-model` for `modelValue`, `v-model:open` for `open` (#1436). Asked with either name,
48
+ * so the two rows of a pair, one under Props and one under Events, carry the same mark.
49
+ */
50
+ function modelLabel(doc, name) {
51
+ const prop = name.startsWith("update:") ? name.slice(7) : name;
52
+ if (!(doc.props.some((candidate) => candidate.name === prop) && doc.events.some((event) => event.name === `update:${prop}`))) return;
53
+ return prop === "modelValue" ? "v-model" : `v-model:${prop}`;
54
+ }
11
55
  function tagText(tags, name) {
12
56
  const tag = tags.find((tag) => tag.name === name);
13
57
  return tag ? tag.text ?? "" : void 0;
@@ -23,23 +67,49 @@ function tagHref(text) {
23
67
  */
24
68
  function useComponentDocs(storyId) {
25
69
  const components = ref();
70
+ const pending = ref(false);
71
+ const off = ref();
26
72
  const hot = import.meta.hot;
27
73
  if (!hot) {
28
- watch(storyId, async (id) => {
29
- const { componentDocs } = await import("./component-docs-data.js");
30
- const docs = id ? componentDocs[id] : void 0;
31
- components.value = id && docs ? toEntries({
32
- storyId: id,
33
- components: docs
34
- }) : void 0;
74
+ watch(storyId, async (id, _, onCleanup) => {
75
+ let stale = false;
76
+ onCleanup(() => stale = true);
77
+ pending.value = true;
78
+ try {
79
+ const { componentDocs } = await import("./component-docs-data.js");
80
+ const loaders = id ? componentDocs[id] : void 0;
81
+ const docs = loaders && Object.fromEntries(await Promise.all(Object.entries(loaders).map(async ([file, load]) => [file, (await load()).default])));
82
+ if (!stale) components.value = id && docs ? toEntries({
83
+ storyId: id,
84
+ components: docs
85
+ }) : void 0;
86
+ } catch (error) {
87
+ if (!stale) {
88
+ components.value = void 0;
89
+ console.error("[poveste] could not load this story's component docs:", error);
90
+ }
91
+ } finally {
92
+ if (!stale) pending.value = false;
93
+ }
35
94
  }, { immediate: true });
36
- return { components };
95
+ return {
96
+ components,
97
+ pending,
98
+ off
99
+ };
37
100
  }
38
101
  function ask() {
39
- if (storyId.value) hot.send("poveste:docgen-request", { storyId: storyId.value });
102
+ if (storyId.value) {
103
+ pending.value = true;
104
+ hot.send("poveste:docgen-request", { storyId: storyId.value });
105
+ }
40
106
  }
41
107
  function onResult(result) {
42
- if (result.storyId === storyId.value) components.value = toEntries(result);
108
+ if (result.storyId === storyId.value) {
109
+ components.value = toEntries(result);
110
+ off.value = result.off;
111
+ pending.value = false;
112
+ }
43
113
  }
44
114
  hot.on("poveste:docgen-result", onResult);
45
115
  hot.on("poveste:docgen-stale", ask);
@@ -49,9 +119,15 @@ function useComponentDocs(storyId) {
49
119
  });
50
120
  watch(storyId, () => {
51
121
  components.value = void 0;
122
+ off.value = void 0;
123
+ pending.value = false;
52
124
  ask();
53
125
  }, { immediate: true });
54
- return { components };
126
+ return {
127
+ components,
128
+ pending,
129
+ off
130
+ };
55
131
  }
56
132
  //#endregion
57
- export { tagHref, tagText, toEntries, useComponentDocs };
133
+ export { modelLabel, onlySections, rootFirst, tagHref, tagText, toEntries, useComponentDocs };
@@ -2,11 +2,13 @@ import { povesteConfig } from "./config.js";
2
2
  import { useDark, useToggle } from "@poveste/vendors/vue-use";
3
3
  import { watch } from "@poveste/vendors/vue";
4
4
  //#region src/app/util/dark.ts
5
+ var inSandbox = location.href.includes("__sandbox");
5
6
  var isDark = useDark({
6
7
  valueDark: "ptw-dark",
7
8
  ...povesteConfig.theme.defaultColorScheme ? { initialValue: povesteConfig.theme.defaultColorScheme } : {},
8
9
  storageKey: "poveste-color-scheme",
9
- storage: povesteConfig.theme.storeColorScheme ? localStorage : sessionStorage
10
+ storage: povesteConfig.theme.storeColorScheme ? localStorage : sessionStorage,
11
+ ...inSandbox ? { onChanged: () => {} } : {}
10
12
  });
11
13
  var toggleDark = useToggle(isDark);
12
14
  function applyDarkToControls() {
@@ -3,7 +3,9 @@ import { clientSupportPlugins } from "virtual:$poveste-support-plugins-client";
3
3
  //#region src/app/util/docs.ts
4
4
  /** What the variant declares, or what its framework generates from it; `undefined` when neither has anything. */
5
5
  async function getDynamicSourceCode(variant, generate) {
6
- if (variant.source) return variant.source;
6
+ const source = variant.source;
7
+ if (typeof source === "function") return source(variant.state) || void 0;
8
+ if (source) return source;
7
9
  const sourceSlot = variant.slots?.().source;
8
10
  if (sourceSlot) {
9
11
  const source = sourceSlot()[0].children;
@@ -11,6 +13,14 @@ async function getDynamicSourceCode(variant, generate) {
11
13
  }
12
14
  return await generate?.(variant) || void 0;
13
15
  }
16
+ /**
17
+ * The grammar a story's source reads in: a Vue or Svelte story is HTML with script
18
+ * inside, and a Solid or React story file is TypeScript with JSX, which the HTML
19
+ * grammar left plain (#1521). A variant's own source follows its story's.
20
+ */
21
+ function sourceLang(story) {
22
+ return /\.[cm]?[jt]sx$/.test(story?.file?.filePath ?? "") ? "tsx" : "html";
23
+ }
14
24
  async function getStaticSourceCode(story) {
15
25
  const sourceLoader = story.file?.source;
16
26
  return sourceLoader ? (await sourceLoader()).default : void 0;
@@ -25,4 +35,4 @@ async function getSourceCode(story, variant) {
25
35
  return await getDynamicSourceCode(variant, generate) || getStaticSourceCode(story);
26
36
  }
27
37
  //#endregion
28
- export { getDynamicSourceCode, getSourceCode, getStaticSourceCode, loadGenerateSourceCode };
38
+ export { getDynamicSourceCode, getSourceCode, getStaticSourceCode, loadGenerateSourceCode, sourceLang };
@@ -17,7 +17,7 @@ var highlighterPromise;
17
17
  */
18
18
  function getHighlighter() {
19
19
  highlighterPromise ??= createHighlighterCore({
20
- langs: [import("shiki/langs/html.mjs"), import("shiki/langs/jsx.mjs")],
20
+ langs: [import("shiki/langs/html.mjs"), import("shiki/langs/tsx.mjs")],
21
21
  themes: [import("shiki/themes/github-light-high-contrast.mjs"), import("shiki/themes/github-dark.mjs")],
22
22
  engine: createOnigurumaEngine(import("shiki/wasm"))
23
23
  }).catch((e) => {
@@ -15,6 +15,7 @@ var iconCollections = [
15
15
  "chevron-left": { "body": "<path fill=\"currentColor\" d=\"M10 16L20 6l1.4 1.4l-8.6 8.6l8.6 8.6L20 26z\"/>" },
16
16
  "chevron-right": { "body": "<path fill=\"currentColor\" d=\"M22 16L12 26l-1.4-1.4l8.6-8.6l-8.6-8.6L12 6z\"/>" },
17
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\"/>" },
18
+ "circle-dash": { "body": "<path fill=\"currentColor\" d=\"M7.7 4.7a14.7 14.7 0 0 0-3 3.1L6.3 9a13.3 13.3 0 0 1 2.6-2.7Zm-3.1 7.6l-1.9-.6A12.5 12.5 0 0 0 2 16h2a11.5 11.5 0 0 1 .6-3.7m-1.9 8.1a14.4 14.4 0 0 0 2 3.9l1.6-1.2a12.9 12.9 0 0 1-1.7-3.3Zm5.1 6.9a14.4 14.4 0 0 0 3.9 2l.6-1.9A12.9 12.9 0 0 1 9 25.7Zm3.9-24.6l.6 1.9A11.5 11.5 0 0 1 16 4V2a12.5 12.5 0 0 0-4.3.7m12.5 24.6a15.2 15.2 0 0 0 3.1-3.1L25.7 23a11.5 11.5 0 0 1-2.7 2.7Zm3.2-7.6l1.9.6A15.5 15.5 0 0 0 30 16h-2a11.5 11.5 0 0 1-.6 3.7m1.8-8.1a14.4 14.4 0 0 0-2-3.9l-1.6 1.2a12.9 12.9 0 0 1 1.7 3.3Zm-5.1-7a14.4 14.4 0 0 0-3.9-2l-.6 1.9a12.9 12.9 0 0 1 3.3 1.7Zm-3.8 24.7l-.6-1.9a11.5 11.5 0 0 1-3.7.6v2a21.4 21.4 0 0 0 4.3-.7\"/>" },
18
19
  "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\"/>" },
19
20
  "code": { "body": "<path fill=\"currentColor\" d=\"m31 16l-7 7l-1.41-1.41L28.17 16l-5.58-5.59L24 9zM1 16l7-7l1.41 1.41L3.83 16l5.58 5.59L8 23zm11.42 9.484L17.64 6l1.932.517L14.352 26z\"/>" },
20
21
  "code-hide": { "body": "<path fill=\"currentColor\" d=\"m17.713 13.471l1.863-6.953L17.645 6l-1.565 5.838zm6.494 6.494l1.414 1.414L31 16l-7-7l-1.414 1.414L28.172 16zM30 28.586L3.414 2L2 3.414l5.793 5.793L1 16l7 7l1.414-1.414L3.828 16l5.379-5.379l5.677 5.677l-2.461 9.184l1.932.518l2.162-8.069L28.586 30z\"/>" },
@@ -72,17 +72,18 @@ function onKeyboardShortcut(shortcut, handler, options = {}) {
72
72
  modifiers.alt.pressed = event.altKey;
73
73
  modifiers.shift.pressed = event.shiftKey;
74
74
  modifiers.meta.pressed = event.metaKey;
75
- if (isMatchingShortcut(isRef(shortcut) ? shortcut.value : shortcut)) handler(event);
75
+ if (isMatchingShortcut(isRef(shortcut) ? shortcut.value : shortcut, event.key.toLocaleLowerCase())) handler(event);
76
76
  });
77
77
  }
78
- function isMatchingShortcut(shortcut) {
79
- for (const combination of shortcut) if (isMatchingCombination(combination.toLowerCase())) return true;
78
+ function isMatchingShortcut(shortcut, eventKey) {
79
+ for (const combination of shortcut) if (isMatchingCombination(combination.toLowerCase(), eventKey)) return true;
80
80
  return false;
81
81
  }
82
- function isMatchingCombination(combination) {
82
+ function isMatchingCombination(combination, eventKey) {
83
83
  const splitted = combination.split("+").map((key) => key.trim());
84
84
  const targetKey = splitted.pop();
85
85
  if (!targetKey) return false;
86
+ if (eventKey !== targetKey) return false;
86
87
  for (const [name, mod] of Object.entries(modifiers)) if (splitted.includes(name) !== mod.pressed) return false;
87
88
  return pressedKeys.has(targetKey);
88
89
  }