@poveste/app 0.5.3 → 0.6.1

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 (90) hide show
  1. package/dist/app/style/font.css +78 -0
  2. package/dist/app/style/main.pcss +4 -1
  3. package/dist/app/style/sandbox.css +3 -0
  4. package/dist/bundle-main-dev.js +1 -0
  5. package/dist/bundle-main.js +3 -0
  6. package/dist/bundled/app.css +28 -20
  7. package/dist/bundled/components/app/AppActions.vue_vue_type_script_setup_true_lang.js +2 -2
  8. package/dist/bundled/components/base/BaseSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/bundled/components/layout/LayoutModal.vue_vue_type_script_setup_true_lang.js +2 -2
  10. package/dist/bundled/components/layout/LayoutPane.vue_vue_type_script_setup_true_lang.js +4 -4
  11. package/dist/bundled/components/panel/StatePresets.vue_vue_type_script_setup_true_lang.js +5 -5
  12. package/dist/bundled/components/panel/StoryControls.vue_vue_type_script_setup_true_lang.js +1 -1
  13. package/dist/bundled/components/panel/StoryDocs.vue_vue_type_script_setup_true_lang.js +1 -1
  14. package/dist/bundled/components/panel/StoryEvent.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/bundled/components/panel/StorySidePanel.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/bundled/components/panel/StorySourceCode.js +1 -1
  17. package/dist/bundled/components/panel/StorySourceCode.vue_vue_type_script_setup_true_lang.js +2 -2
  18. package/dist/bundled/components/search/SearchItem.js +1 -1
  19. package/dist/bundled/components/search/SearchItem.vue_vue_type_script_setup_true_lang.js +2 -2
  20. package/dist/bundled/components/search/SearchModal.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/bundled/components/search/SearchPane.vue_vue_type_script_setup_true_lang.js +1 -0
  22. package/dist/bundled/components/story/StoryResponsivePreview.js +1 -1
  23. package/dist/bundled/components/story/StoryResponsivePreview.vue_vue_type_script_setup_true_lang.js +69 -64
  24. package/dist/bundled/components/story/StoryVariantGrid.js +3 -1
  25. package/dist/bundled/components/story/StoryVariantGrid.vue_vue_type_script_setup_true_lang.js +82 -35
  26. package/dist/bundled/components/story/StoryVariantGridItem.js +1 -1
  27. package/dist/bundled/components/story/StoryVariantGridItem.vue_vue_type_script_setup_true_lang.js +20 -12
  28. package/dist/bundled/components/story/StoryVariantListItem.js +1 -1
  29. package/dist/bundled/components/story/StoryVariantListItem.vue_vue_type_script_setup_true_lang.js +2 -2
  30. package/dist/bundled/components/story/StoryVariantSinglePreviewNative.vue_vue_type_script_setup_true_lang.js +1 -1
  31. package/dist/bundled/components/story/StoryVariantSinglePreviewRemote.vue_vue_type_script_setup_true_lang.js +97 -32
  32. package/dist/bundled/components/story/StoryVariantSingleView.vue_vue_type_script_setup_true_lang.js +1 -1
  33. package/dist/bundled/components/toolbar/ToolbarBackground.js +1 -1
  34. package/dist/bundled/components/toolbar/ToolbarBackground.vue_vue_type_script_setup_true_lang.js +7 -7
  35. package/dist/bundled/components/tree/StoryGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  36. package/dist/bundled/components/tree/StoryListFolder.js +1 -1
  37. package/dist/bundled/components/tree/StoryListFolder.vue_vue_type_script_setup_true_lang.js +2 -2
  38. package/dist/bundled/components/tree/StoryListItem.js +1 -1
  39. package/dist/bundled/components/tree/StoryListItem.vue_vue_type_script_setup_true_lang.js +3 -3
  40. package/dist/bundled/index.js +3 -0
  41. package/dist/bundled/sandbox.js +53 -24
  42. package/dist/bundled/util/const.js +2 -1
  43. package/dist/bundled/util/grid-cell-height.js +18 -0
  44. package/dist/bundled/util/grid-slots.js +64 -0
  45. package/dist/bundled/util/icons.generated.js +104 -0
  46. package/dist/bundled/util/state-bridge.js +75 -0
  47. package/dist/bundled/util/state.js +19 -1
  48. package/dist/style.css +7 -2
  49. package/package.json +12 -5
  50. package/src/__tests__/font.spec.ts +30 -0
  51. package/src/__tests__/grid-slots.spec.ts +84 -0
  52. package/src/__tests__/icons.spec.ts +26 -0
  53. package/src/__tests__/state-bridge.spec.ts +155 -0
  54. package/src/__tests__/state-clone.spec.ts +66 -0
  55. package/src/app/components/app/AppActions.vue +2 -2
  56. package/src/app/components/layout/LayoutModal.vue +2 -2
  57. package/src/app/components/layout/LayoutPane.vue +4 -4
  58. package/src/app/components/panel/StatePresets.vue +5 -5
  59. package/src/app/components/panel/StoryControls.vue +1 -1
  60. package/src/app/components/panel/StoryDocs.vue +1 -1
  61. package/src/app/components/panel/StoryEvent.vue +1 -1
  62. package/src/app/components/panel/StorySidePanel.vue +1 -1
  63. package/src/app/components/panel/StorySourceCode.vue +2 -2
  64. package/src/app/components/search/SearchItem.vue +1 -1
  65. package/src/app/components/search/SearchModal.vue +1 -1
  66. package/src/app/components/search/SearchPane.vue +8 -0
  67. package/src/app/components/story/StoryResponsivePreview.vue +85 -50
  68. package/src/app/components/story/StoryVariantGrid.vue +126 -49
  69. package/src/app/components/story/StoryVariantGridItem.vue +35 -10
  70. package/src/app/components/story/StoryVariantListItem.vue +1 -1
  71. package/src/app/components/story/StoryVariantSinglePreviewNative.vue +1 -1
  72. package/src/app/components/story/StoryVariantSinglePreviewRemote.vue +165 -35
  73. package/src/app/components/story/StoryVariantSingleView.vue +1 -1
  74. package/src/app/components/toolbar/ToolbarBackground.vue +4 -4
  75. package/src/app/components/tree/StoryGroup.vue +1 -1
  76. package/src/app/components/tree/StoryListFolder.vue +1 -1
  77. package/src/app/components/tree/StoryListItem.vue +1 -1
  78. package/src/app/index.ts +10 -0
  79. package/src/app/sandbox.ts +95 -31
  80. package/src/app/style/font.css +78 -0
  81. package/src/app/style/main.pcss +4 -1
  82. package/src/app/style/sandbox.css +3 -0
  83. package/src/app/util/const.ts +1 -0
  84. package/src/app/util/grid-cell-height.ts +16 -0
  85. package/src/app/util/grid-slots.ts +72 -0
  86. package/src/app/util/icons.generated.ts +9 -0
  87. package/src/app/util/state-bridge.ts +95 -0
  88. package/src/app/util/state.ts +19 -0
  89. package/src/bundle-main-dev.js +1 -0
  90. package/src/bundle-main.js +3 -0
@@ -1,5 +1,7 @@
1
+ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export-helper.js";
1
2
  import StoryVariantGrid_vue_vue_type_script_setup_true_lang_default from "./StoryVariantGrid.vue_vue_type_script_setup_true_lang.js";
3
+ /* empty css */
2
4
  //#region src/app/components/story/StoryVariantGrid.vue
3
- var StoryVariantGrid_default = StoryVariantGrid_vue_vue_type_script_setup_true_lang_default;
5
+ var StoryVariantGrid_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryVariantGrid_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-15980005"]]);
4
6
  //#endregion
5
7
  export { StoryVariantGrid_default as default };
@@ -1,12 +1,15 @@
1
1
  import { useStoryStore } from "../../stores/story.js";
2
+ import { assignSlots, trimSlots } from "../../util/grid-slots.js";
2
3
  import StoryVariantGridItem_default from "./StoryVariantGridItem.js";
3
4
  import { useResizeObserver } from "@poveste/vendors/vue-use";
4
- import { Fragment, computed, createBlock, createElementBlock, createElementVNode, defineComponent, normalizeStyle, onMounted, openBlock, ref, renderList, unref, watch } from "@poveste/vendors/vue";
5
+ import { Fragment, computed, createBlock, createElementBlock, createElementVNode, defineComponent, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, unref, watch } from "@poveste/vendors/vue";
5
6
  //#region src/app/components/story/StoryVariantGrid.vue?vue&type=script&setup=true&lang.ts
6
7
  var _hoisted_1 = { class: "poveste-story-variant-grid flex flex-col items-stretch h-full __poveste-pane-shadow-from-right" };
7
8
  var _hoisted_2 = { class: "flex w-0 flex-1 mx-4" };
8
- var margin = 16;
9
9
  var gap = 16;
10
+ var overscanRows = 2;
11
+ var initialCount = 10;
12
+ var TRIM_DELAY = 2e3;
10
13
  var StoryVariantGrid_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
11
14
  __name: "StoryVariantGrid",
12
15
  setup(__props) {
@@ -18,41 +21,26 @@ var StoryVariantGrid_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
18
21
  if (typeof layoutWidth === "number") return `${layoutWidth}px`;
19
22
  return layoutWidth;
20
23
  });
21
- const itemWidth = ref(16);
22
24
  const maxItemHeight = ref(0);
23
- const maxCount = ref(10);
24
- const countPerRow = ref(0);
25
- const visibleRows = ref(0);
26
25
  const el = ref(null);
26
+ const scrollTop = ref(0);
27
+ const viewportHeight = ref(0);
28
+ const variants = computed(() => storyStore.currentStory.variants);
27
29
  useResizeObserver(el, () => {
28
- updateMaxCount();
30
+ updateWindow();
29
31
  updateSize();
30
32
  });
31
- function updateMaxCount() {
32
- if (!maxItemHeight.value) return;
33
- const width = el.value.clientWidth - margin * 2;
34
- const height = el.value.clientHeight;
35
- const scrollTop = el.value.scrollTop;
36
- countPerRow.value = Math.floor((width + gap) / (itemWidth.value + gap));
37
- visibleRows.value = Math.ceil((height + scrollTop + gap) / (maxItemHeight.value + gap));
38
- const newMaxCount = countPerRow.value * visibleRows.value;
39
- if (maxCount.value < newMaxCount) maxCount.value = newMaxCount;
40
- if (storyStore.currentVariant) {
41
- const index = storyStore.currentStory.variants.indexOf(storyStore.currentVariant);
42
- if (index + 1 > maxCount.value) maxCount.value = index + 1;
43
- }
33
+ function updateWindow() {
34
+ if (!el.value) return;
35
+ scrollTop.value = el.value.scrollTop;
36
+ viewportHeight.value = el.value.clientHeight;
44
37
  }
45
- function onItemResize(w, h) {
46
- itemWidth.value = w;
38
+ function onItemResize(_w, h) {
47
39
  if (maxItemHeight.value < h) {
48
40
  maxItemHeight.value = h;
49
- updateMaxCount();
41
+ updateWindow();
50
42
  }
51
43
  }
52
- watch(() => storyStore.currentVariant, () => {
53
- maxItemHeight.value = 0;
54
- updateMaxCount();
55
- });
56
44
  const gridEl = ref(null);
57
45
  const gridColumnWidth = ref(1);
58
46
  const viewWidth = ref(1);
@@ -65,32 +53,91 @@ var StoryVariantGrid_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
65
53
  }
66
54
  onMounted(() => {
67
55
  updateSize();
56
+ updateWindow();
68
57
  });
69
58
  useResizeObserver(gridEl, () => {
70
59
  updateSize();
71
60
  });
72
- const columnCount = computed(() => Math.min(storyStore.currentStory.variants.length, Math.floor((viewWidth.value + gap) / (gridColumnWidth.value + gap))));
61
+ const columnCount = computed(() => Math.max(1, Math.min(variants.value.length, Math.floor((viewWidth.value + gap) / (gridColumnWidth.value + gap)))));
62
+ const rowHeight = computed(() => maxItemHeight.value + gap);
63
+ const measured = computed(() => maxItemHeight.value > 0);
64
+ const rowCount = computed(() => Math.ceil(variants.value.length / columnCount.value));
65
+ const startIndex = computed(() => {
66
+ if (!measured.value) return 0;
67
+ const firstRow = Math.floor(scrollTop.value / rowHeight.value) - overscanRows;
68
+ return Math.max(0, firstRow) * columnCount.value;
69
+ });
70
+ const endIndex = computed(() => {
71
+ if (!measured.value) return Math.min(variants.value.length, initialCount);
72
+ const lastRow = Math.ceil((scrollTop.value + viewportHeight.value) / rowHeight.value) + overscanRows;
73
+ return Math.min(variants.value.length, Math.max(lastRow, 1) * columnCount.value);
74
+ });
75
+ const visibleVariants = computed(() => variants.value.slice(startIndex.value, endIndex.value));
76
+ const slots = ref([]);
77
+ let trimTimer;
78
+ function scheduleTrim() {
79
+ clearTimeout(trimTimer);
80
+ trimTimer = setTimeout(() => {
81
+ if (!measured.value) {
82
+ scheduleTrim();
83
+ return;
84
+ }
85
+ slots.value = trimSlots(slots.value);
86
+ }, TRIM_DELAY);
87
+ }
88
+ watch(visibleVariants, (visible) => {
89
+ slots.value = assignSlots(slots.value, storyStore.currentStory.id, visible);
90
+ scheduleTrim();
91
+ }, { immediate: true });
92
+ onBeforeUnmount(() => {
93
+ clearTimeout(trimTimer);
94
+ });
95
+ const totalHeight = computed(() => measured.value ? rowCount.value * rowHeight.value - gap : void 0);
96
+ const offsetY = computed(() => measured.value ? startIndex.value / columnCount.value * rowHeight.value : 0);
97
+ watch(() => storyStore.currentVariant, (variant) => {
98
+ maxItemHeight.value = 0;
99
+ updateWindow();
100
+ if (!variant || !el.value || !measured.value) return;
101
+ const index = variants.value.indexOf(variant);
102
+ if (index === -1) return;
103
+ const top = Math.floor(index / columnCount.value) * rowHeight.value;
104
+ if (top < scrollTop.value || top + rowHeight.value > scrollTop.value + viewportHeight.value) {
105
+ el.value.scrollTop = top;
106
+ updateWindow();
107
+ }
108
+ });
73
109
  return (_ctx, _cache) => {
74
110
  return openBlock(), createElementBlock("div", _hoisted_1, [createElementVNode("div", {
75
111
  ref_key: "el",
76
112
  ref: el,
77
113
  class: "overflow-y-auto flex flex-1",
78
- onScroll: _cache[0] || (_cache[0] = ($event) => updateMaxCount())
114
+ onScroll: _cache[0] || (_cache[0] = ($event) => updateWindow())
79
115
  }, [createElementVNode("div", _hoisted_2, [createElementVNode("div", {
80
116
  class: "m-auto",
81
- style: normalizeStyle({ minHeight: `${unref(storyStore).currentStory.variants.length / countPerRow.value * (maxItemHeight.value + gap) - gap}px` })
117
+ style: normalizeStyle({ minHeight: totalHeight.value != null ? `${totalHeight.value}px` : void 0 })
82
118
  }, [createElementVNode("div", {
83
119
  ref_key: "gridEl",
84
120
  ref: gridEl,
85
- class: "grid gap-4 my-4",
86
- style: normalizeStyle({ gridTemplateColumns: `repeat(${columnCount.value}, ${gridColumnWidth.value}px)` })
87
- }, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(storyStore).currentStory.variants.slice(0, maxCount.value), (variant, index) => {
121
+ class: "poveste-story-variant-grid-cells grid gap-4 my-4",
122
+ style: normalizeStyle({
123
+ gridTemplateColumns: `repeat(${columnCount.value}, ${gridColumnWidth.value}px)`,
124
+ transform: offsetY.value ? `translateY(${offsetY.value}px)` : void 0
125
+ })
126
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(slots.value, (slot, index) => {
88
127
  return openBlock(), createBlock(StoryVariantGridItem_default, {
89
128
  key: index,
90
- variant,
129
+ variant: slot.variant,
91
130
  story: unref(storyStore).currentStory,
131
+ style: normalizeStyle({
132
+ order: slot.order,
133
+ display: slot.visible ? void 0 : "none"
134
+ }),
92
135
  onResize: onItemResize
93
- }, null, 8, ["variant", "story"]);
136
+ }, null, 8, [
137
+ "variant",
138
+ "story",
139
+ "style"
140
+ ]);
94
141
  }), 128))], 4)], 4)])], 544)]);
95
142
  };
96
143
  }
@@ -2,6 +2,6 @@ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export
2
2
  import StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default from "./StoryVariantGridItem.vue_vue_type_script_setup_true_lang.js";
3
3
  /* empty css */
4
4
  //#region src/app/components/story/StoryVariantGridItem.vue
5
- var StoryVariantGridItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-74c04c7c"]]);
5
+ var StoryVariantGridItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-a394f172"]]);
6
6
  //#endregion
7
7
  export { StoryVariantGridItem_default as default };
@@ -11,7 +11,7 @@ import { useCurrentVariantRoute } from "../../util/variant.js";
11
11
  import CheckerboardPattern_default from "../misc/CheckerboardPattern.js";
12
12
  import StoryVariantSinglePreviewRemote_default from "./StoryVariantSinglePreviewRemote.js";
13
13
  import { useResizeObserver } from "@poveste/vendors/vue-use";
14
- import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, ref, resolveComponent, resolveDirective, toDisplayString, toRefs, unref, useCssVars, withCtx, withDirectives, withModifiers } from "@poveste/vendors/vue";
14
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, openBlock, ref, resolveComponent, resolveDirective, toDisplayString, toRefs, unref, useCssVars, watch, withCtx, withDirectives, withModifiers } from "@poveste/vendors/vue";
15
15
  import { Icon } from "@poveste/vendors/iconify";
16
16
  import { useRouter } from "@poveste/vendors/vue-router";
17
17
  import { HstCopyIcon } from "@poveste/controls";
@@ -34,8 +34,8 @@ var StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
34
34
  emits: { resize: (_width, _height) => true },
35
35
  setup(__props, { emit: __emit }) {
36
36
  useCssVars((_ctx) => ({
37
- "v2e50e6e6": unref(variant).iconColor,
38
- "v7d5058c5": unref(settings).backgroundColor
37
+ "v68878cb8": unref(variant).iconColor,
38
+ "v5a063a9a": unref(settings).backgroundColor
39
39
  }));
40
40
  const props = __props;
41
41
  const emit = __emit;
@@ -43,6 +43,10 @@ var StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
43
43
  const { isActive, targetRoute } = useCurrentVariantRoute(variant);
44
44
  const storyStore = useStoryStore();
45
45
  storyStore.setPreviewReady(props.variant, false);
46
+ watch(() => props.variant, (next, previous) => {
47
+ if (previous) storyStore.setPreviewReady(previous, false);
48
+ storyStore.setPreviewReady(next, false);
49
+ });
46
50
  function onReady() {
47
51
  storyStore.setPreviewReady(props.variant, true);
48
52
  }
@@ -52,11 +56,14 @@ var StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
52
56
  }
53
57
  const el = ref();
54
58
  const { autoScroll } = useScrollOnActive(isActive, el);
55
- useResizeObserver(el, () => {
56
- if (props.variant.previewReady) {
57
- emit("resize", el.value.clientWidth, el.value.clientHeight);
58
- if (isActive.value) autoScroll();
59
- }
59
+ function reportSize() {
60
+ if (!el.value || !props.variant.previewReady) return;
61
+ emit("resize", el.value.clientWidth, el.value.clientHeight);
62
+ if (isActive.value) autoScroll();
63
+ }
64
+ useResizeObserver(el, reportSize);
65
+ watch(() => props.variant.previewReady, (ready) => {
66
+ if (ready) nextTick(reportSize);
60
67
  });
61
68
  const settings = usePreviewSettingsStore().currentSettings;
62
69
  const previewDark = usePreviewDark(settings);
@@ -97,13 +104,13 @@ var StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
97
104
  "border-gray-100 dark:border-gray-800": !unref(isActive),
98
105
  "border-primary-200 dark:border-primary-900": unref(isActive)
99
106
  }]),
100
- "data-test-id": "sandbox-render",
107
+ "data-testid": "sandbox-render",
101
108
  onClick: _cache[0] || (_cache[0] = withModifiers(($event) => selectVariant(), ["stop"])),
102
109
  onKeyup: _cache[1] || (_cache[1] = ($event) => selectVariant())
103
110
  }, [
104
111
  _cache[2] || (_cache[2] = createElementVNode("div", {
105
112
  class: "absolute inset-0 rounded bind-preview-bg",
106
- "data-test-id": "responsive-preview-bg"
113
+ "data-testid": "responsive-preview-bg"
107
114
  }, null, -1)),
108
115
  unref(settings).checkerboard ? (openBlock(), createBlock(CheckerboardPattern_default, {
109
116
  key: 0,
@@ -117,10 +124,11 @@ var StoryVariantGridItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
117
124
  "color": autoApplyContrastColor.value ? contrastColor.value : void 0
118
125
  })
119
126
  }, [useIframe.value ? (openBlock(), createBlock(StoryVariantSinglePreviewRemote_default, {
120
- key: `iframe-${__props.story.id}-${unref(variant).id}`,
127
+ key: 0,
121
128
  story: __props.story,
122
129
  variant: unref(variant),
123
- "auto-height": ""
130
+ "auto-height": "",
131
+ responsive: false
124
132
  }, null, 8, ["story", "variant"])) : (openBlock(), createBlock(GenericRenderStory_default, {
125
133
  key: `${__props.story.id}-${unref(variant).id}`,
126
134
  variant: unref(variant),
@@ -2,6 +2,6 @@ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export
2
2
  import StoryVariantListItem_vue_vue_type_script_setup_true_lang_default from "./StoryVariantListItem.vue_vue_type_script_setup_true_lang.js";
3
3
  /* empty css */
4
4
  //#region src/app/components/story/StoryVariantListItem.vue
5
- var StoryVariantListItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryVariantListItem_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-37853789"]]);
5
+ var StoryVariantListItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryVariantListItem_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e5d28031"]]);
6
6
  //#endregion
7
7
  export { StoryVariantListItem_default as default };
@@ -12,7 +12,7 @@ var StoryVariantListItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
12
12
  required: true
13
13
  } },
14
14
  setup(__props) {
15
- useCssVars((_ctx) => ({ "v25a47cf6": unref(variant).iconColor }));
15
+ useCssVars((_ctx) => ({ "v97886b12": unref(variant).iconColor }));
16
16
  const { variant } = toRefs(__props);
17
17
  const { isActive, targetRoute } = useCurrentVariantRoute(variant);
18
18
  const el = ref();
@@ -22,7 +22,7 @@ var StoryVariantListItem_vue_vue_type_script_setup_true_lang_default = /*@__PURE
22
22
  ref_key: "el",
23
23
  ref: el,
24
24
  class: "poveste-story-variant-list-item",
25
- "data-test-id": "story-variant-list-item"
25
+ "data-testid": "story-variant-list-item"
26
26
  }, [createVNode(BaseListItemLink_default, {
27
27
  to: unref(targetRoute),
28
28
  "is-active": unref(isActive),
@@ -42,7 +42,7 @@ var StoryVariantSinglePreviewNative_vue_vue_type_script_setup_true_lang_default
42
42
  "color": autoApplyContrastColor.value ? contrastColor.value : void 0
43
43
  }]),
44
44
  class: "relative",
45
- "data-test-id": "sandbox-render"
45
+ "data-testid": "sandbox-render"
46
46
  }, [(openBlock(), createBlock(GenericRenderStory_default, {
47
47
  key: `${__props.story.id}-${__props.variant.id}`,
48
48
  variant: __props.variant,
@@ -1,91 +1,148 @@
1
- import { EVENT_SEND, PREVIEW_SETTINGS_REQUEST, PREVIEW_SETTINGS_SYNC, SANDBOX_HEIGHT, SANDBOX_READY, STATE_SYNC } from "../../util/const.js";
1
+ import { EVENT_SEND, PREVIEW_SETTINGS_REQUEST, PREVIEW_SETTINGS_SYNC, SANDBOX_HEIGHT, SANDBOX_READY, SANDBOX_RETARGET, STATE_SYNC } from "../../util/const.js";
2
2
  import { trackWindow } from "../../util/keyboard.js";
3
3
  import { useStoryStore } from "../../stores/story.js";
4
4
  import { usePreviewSettingsStore } from "../../stores/preview-settings.js";
5
5
  import { getSandboxUrl } from "../../util/sandbox.js";
6
+ import { createStateBridge } from "../../util/state-bridge.js";
6
7
  import { toRawDeep } from "../../util/state.js";
7
8
  import { useEventsStore } from "../../stores/events.js";
9
+ import { firstReportedHeight } from "../../util/grid-cell-height.js";
8
10
  import StoryResponsivePreview_default from "./StoryResponsivePreview.js";
9
11
  import { useEventListener } from "@poveste/vendors/vue-use";
10
12
  import { computed, createBlock, createElementVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, ref, toRaw, watch, withCtx } from "@poveste/vendors/vue";
11
- import { applyState } from "@poveste/shared";
12
13
  //#region src/app/components/story/StoryVariantSinglePreviewRemote.vue?vue&type=script&setup=true&lang.ts
13
14
  var _hoisted_1 = ["src"];
15
+ var MIN_HEIGHT = 40;
16
+ var MAX_AUTO_HEIGHT = 400;
17
+ var REUSE_LIMIT = 30;
18
+ var RETARGET_TIMEOUT = 8e3;
14
19
  var StoryVariantSinglePreviewRemote_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
15
20
  __name: "StoryVariantSinglePreviewRemote",
16
21
  props: {
17
22
  story: {},
18
23
  variant: {},
19
- autoHeight: { type: Boolean }
24
+ autoHeight: { type: Boolean },
25
+ responsive: {
26
+ type: Boolean,
27
+ default: true
28
+ }
20
29
  },
21
30
  setup(__props) {
22
31
  const props = __props;
23
32
  const settings = usePreviewSettingsStore().currentSettings;
24
33
  const iframe = ref();
25
- function syncState() {
26
- if (iframe.value?.contentWindow && props.variant.previewReady) iframe.value.contentWindow.postMessage({
34
+ function postState(changes) {
35
+ iframe.value?.contentWindow?.postMessage({
27
36
  type: STATE_SYNC,
28
- state: toRawDeep(props.variant.state, true)
37
+ state: changes
29
38
  });
30
39
  }
31
- let synced = false;
32
- watch(() => props.variant.state, () => {
33
- if (synced) {
34
- synced = false;
35
- return;
36
- }
40
+ let bridge = createStateBridge(postState);
41
+ function syncState() {
42
+ if (!iframe.value?.contentWindow || !props.variant.previewReady) return;
43
+ bridge.send(toRawDeep(props.variant.state, true));
44
+ }
45
+ watch(() => props.variant.state, (state, previous) => {
46
+ if (state !== previous) return;
37
47
  syncState();
38
- }, {
39
- deep: true,
40
- immediate: true
41
- });
48
+ }, { deep: true });
42
49
  const storyStore = useStoryStore();
43
50
  storyStore.setPreviewReady(props.variant, false);
44
51
  const reportedHeight = ref(null);
52
+ const placeholderHeight = computed(() => firstReportedHeight.value?.storyId === props.story.id ? firstReportedHeight.value.height : MIN_HEIGHT);
53
+ function fromCurrentOccupant(data) {
54
+ return !data.variantId || data.variantId === props.variant.id && data.storyId === props.story.id;
55
+ }
45
56
  useEventListener(window, "message", (event) => {
46
57
  if (event.source !== iframe.value?.contentWindow) return;
47
58
  switch (event.data?.type) {
48
59
  case STATE_SYNC:
60
+ if (!fromCurrentOccupant(event.data)) break;
49
61
  updateVariantState(event.data.state);
50
62
  break;
51
63
  case EVENT_SEND:
52
64
  logEvent(event.data.event);
53
65
  break;
54
66
  case SANDBOX_READY:
67
+ if (!fromCurrentOccupant(event.data)) break;
55
68
  setPreviewReady();
56
69
  break;
57
70
  case PREVIEW_SETTINGS_REQUEST:
58
71
  syncSettings();
59
72
  break;
60
- case SANDBOX_HEIGHT: if (typeof event.data.h === "number") {
61
- const next = Math.max(40, event.data.h);
62
- if (next !== reportedHeight.value) reportedHeight.value = next;
63
- }
73
+ case SANDBOX_HEIGHT:
74
+ if (!fromCurrentOccupant(event.data)) break;
75
+ if (typeof event.data.h === "number" && event.data.h >= MIN_HEIGHT) {
76
+ const next = event.data.h;
77
+ if (next !== reportedHeight.value) reportedHeight.value = next;
78
+ if (props.autoHeight && firstReportedHeight.value?.storyId !== props.story.id) firstReportedHeight.value = {
79
+ storyId: props.story.id,
80
+ height: next
81
+ };
82
+ }
64
83
  }
65
84
  });
66
85
  function updateVariantState(state) {
67
- synced = applyState(props.variant.state, state);
86
+ bridge.receive(props.variant.state, state);
68
87
  }
69
88
  function logEvent(event) {
70
89
  useEventsStore().addEvent(event);
71
90
  }
91
+ const retargeting = ref(false);
92
+ let retargetTimer;
72
93
  function setPreviewReady() {
94
+ retargeting.value = false;
95
+ clearTimeout(retargetTimer);
73
96
  storyStore.setPreviewReady(props.variant, true);
97
+ syncState();
74
98
  }
75
99
  const sandboxUrl = computed(() => {
76
100
  return getSandboxUrl(props.story, props.variant);
77
101
  });
102
+ const iframeSrc = ref(sandboxUrl.value);
78
103
  const isIframeLoaded = ref(false);
104
+ let retargets = 0;
105
+ let servedStory = props.story;
79
106
  let stopTrackKeyboard;
80
107
  let unmounted = false;
81
- watch(sandboxUrl, () => {
108
+ function reload(url) {
109
+ retargets = 0;
110
+ retargeting.value = false;
111
+ clearTimeout(retargetTimer);
82
112
  isIframeLoaded.value = false;
83
- storyStore.setPreviewReady(props.variant, false);
113
+ reportedHeight.value = null;
114
+ servedStory = props.story;
84
115
  stopTrackKeyboard?.();
85
116
  stopTrackKeyboard = void 0;
117
+ if (iframeSrc.value === url && iframe.value) iframe.value.src = url;
118
+ iframeSrc.value = url;
119
+ }
120
+ watch(sandboxUrl, (url) => {
121
+ bridge = createStateBridge(postState);
122
+ storyStore.setPreviewReady(props.variant, false);
123
+ const reuse = props.story.layout?.isolate !== true && servedStory.layout?.isolate !== true;
124
+ const target = iframe.value?.contentWindow;
125
+ if (reuse && isIframeLoaded.value && target && retargets < REUSE_LIMIT) {
126
+ if (servedStory.id !== props.story.id) reportedHeight.value = null;
127
+ servedStory = props.story;
128
+ retargets++;
129
+ retargeting.value = true;
130
+ target.postMessage({
131
+ type: SANDBOX_RETARGET,
132
+ storyId: props.story.id,
133
+ variantId: props.variant.id
134
+ }, window.location.origin);
135
+ clearTimeout(retargetTimer);
136
+ retargetTimer = setTimeout(() => {
137
+ if (retargeting.value && !unmounted) reload(url);
138
+ }, RETARGET_TIMEOUT);
139
+ return;
140
+ }
141
+ reload(url);
86
142
  });
87
143
  onBeforeUnmount(() => {
88
144
  unmounted = true;
145
+ clearTimeout(retargetTimer);
89
146
  stopTrackKeyboard?.();
90
147
  });
91
148
  function syncSettings() {
@@ -100,8 +157,18 @@ var StoryVariantSinglePreviewRemote_vue_vue_type_script_setup_true_lang_default
100
157
  deep: true,
101
158
  immediate: true
102
159
  });
160
+ function previewStyle(isResponsiveEnabled, finalWidth, finalHeight) {
161
+ const style = {};
162
+ if (isResponsiveEnabled) {
163
+ if (finalWidth) style.width = `${finalWidth}px`;
164
+ if (finalHeight) style.height = `${finalHeight}px`;
165
+ }
166
+ if (props.autoHeight) style.height = `${Math.min(reportedHeight.value ?? placeholderHeight.value, MAX_AUTO_HEIGHT)}px`;
167
+ return style;
168
+ }
103
169
  function onIframeLoad() {
104
170
  if (unmounted) return;
171
+ retargets = 0;
105
172
  isIframeLoaded.value = true;
106
173
  syncState();
107
174
  syncSettings();
@@ -111,25 +178,23 @@ var StoryVariantSinglePreviewRemote_vue_vue_type_script_setup_true_lang_default
111
178
  return (_ctx, _cache) => {
112
179
  return openBlock(), createBlock(StoryResponsivePreview_default, {
113
180
  class: "poveste-story-variant-single-preview-remote",
114
- variant: __props.variant
181
+ variant: __props.variant,
182
+ responsive: __props.responsive
115
183
  }, {
116
184
  default: withCtx(({ isResponsiveEnabled, finalWidth, finalHeight, resizing }) => [createElementVNode("iframe", {
117
185
  ref_key: "iframe",
118
186
  ref: iframe,
119
- src: sandboxUrl.value,
187
+ src: iframeSrc.value,
120
188
  class: normalizeClass(["w-full h-full relative", {
121
- "invisible": !isIframeLoaded.value,
189
+ "invisible": !isIframeLoaded.value || retargeting.value,
122
190
  "pointer-events-none": resizing
123
191
  }]),
124
- style: normalizeStyle(isResponsiveEnabled ? {
125
- width: finalWidth ? `${finalWidth}px` : null,
126
- height: finalHeight ? `${finalHeight}px` : null
127
- } : props.autoHeight && reportedHeight.value ? { height: `${reportedHeight.value}px` } : void 0),
128
- "data-test-id": "preview-iframe",
192
+ style: normalizeStyle(previewStyle(isResponsiveEnabled, finalWidth, finalHeight)),
193
+ "data-testid": "preview-iframe",
129
194
  onLoad: _cache[0] || (_cache[0] = ($event) => onIframeLoad())
130
195
  }, null, 46, _hoisted_1)]),
131
196
  _: 1
132
- }, 8, ["variant"]);
197
+ }, 8, ["variant", "responsive"]);
133
198
  };
134
199
  }
135
200
  });
@@ -4,7 +4,7 @@ import { createBlock, createElementBlock, defineComponent, openBlock } from "@po
4
4
  //#region src/app/components/story/StoryVariantSingleView.vue?vue&type=script&setup=true&lang.ts
5
5
  var _hoisted_1 = {
6
6
  class: "poveste-story-variant-single-view h-full flex flex-col",
7
- "data-test-id": "story-variant-single-view"
7
+ "data-testid": "story-variant-single-view"
8
8
  };
9
9
  var StoryVariantSingleView_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
10
10
  __name: "StoryVariantSingleView",
@@ -2,6 +2,6 @@ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export
2
2
  import ToolbarBackground_vue_vue_type_script_setup_true_lang_default from "./ToolbarBackground.vue_vue_type_script_setup_true_lang.js";
3
3
  /* empty css */
4
4
  //#region src/app/components/toolbar/ToolbarBackground.vue
5
- var ToolbarBackground_default = /*#__PURE__*/ _plugin_vue_export_helper_default(ToolbarBackground_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-d6bd6512"]]);
5
+ var ToolbarBackground_default = /*#__PURE__*/ _plugin_vue_export_helper_default(ToolbarBackground_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-ed785ad4"]]);
6
6
  //#endregion
7
7
  export { ToolbarBackground_default as default };
@@ -7,14 +7,14 @@ import { Icon } from "@poveste/vendors/iconify";
7
7
  var _hoisted_1 = { class: "flex items-center gap-1 px-2.5 py-1.5 hover:bg-white/50 dark:hover:bg-white/10 hover:text-primary-500 dark:hover:text-primary-400 cursor-pointer text-gray-900 dark:text-gray-100 transition-colors group" };
8
8
  var _hoisted_2 = {
9
9
  class: "flex flex-col items-stretch",
10
- "data-test-id": "background-popper"
10
+ "data-testid": "background-popper"
11
11
  };
12
12
  var _hoisted_3 = {
13
13
  key: 0,
14
14
  class: "flex items-stretch gap-1 px-2 pt-2",
15
- "data-test-id": "sandbox-color-scheme"
15
+ "data-testid": "sandbox-color-scheme"
16
16
  };
17
- var _hoisted_4 = ["data-test-id", "onClick"];
17
+ var _hoisted_4 = ["data-testid", "onClick"];
18
18
  var _hoisted_5 = ["onClick"];
19
19
  var _hoisted_6 = { class: "mr-auto" };
20
20
  var _hoisted_7 = { class: "ml-auto opacity-70" };
@@ -23,8 +23,8 @@ var ToolbarBackground_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
23
23
  __name: "ToolbarBackground",
24
24
  setup(__props) {
25
25
  useCssVars((_ctx) => ({
26
- "v37e53e34": unref(settings).backgroundColor,
27
- "eaa47ed2": _ctx.contrastColor
26
+ "ca113a48": unref(settings).backgroundColor,
27
+ "v0083f4ef": _ctx.contrastColor
28
28
  }));
29
29
  const previewSettings = usePreviewSettingsStore();
30
30
  const settings = previewSettings.currentSettings;
@@ -55,7 +55,7 @@ var ToolbarBackground_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
55
55
  placement: "bottom-end",
56
56
  skidding: 6,
57
57
  class: "poveste-toolbar-background flex-none",
58
- "data-test-id": "toolbar-background"
58
+ "data-testid": "toolbar-background"
59
59
  }, {
60
60
  default: withCtx(({ shown }) => [withDirectives((openBlock(), createElementBlock("div", _hoisted_1, [createVNode(unref(Icon), {
61
61
  icon: "carbon:color-palette",
@@ -68,7 +68,7 @@ var ToolbarBackground_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
68
68
  showColorScheme ? (openBlock(), createElementBlock("div", _hoisted_3, [(openBlock(), createElementBlock(Fragment, null, renderList(colorSchemeOptions, (option) => {
69
69
  return createElementVNode("button", {
70
70
  key: option.value,
71
- "data-test-id": `sandbox-color-scheme-${option.value}`,
71
+ "data-testid": `sandbox-color-scheme-${option.value}`,
72
72
  class: normalizeClass(["flex-1 px-3 py-1.5 rounded flex items-center justify-center gap-1.5 cursor-pointer text-sm", [unref(settings).colorScheme === option.value ? "bg-primary-500 hover:bg-primary-600 text-white dark:text-black" : "bg-transparent hover:bg-primary-100 dark:hover:bg-primary-700"]]),
73
73
  onClick: ($event) => unref(settings).colorScheme = option.value
74
74
  }, [createVNode(unref(Icon), {
@@ -5,7 +5,7 @@ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock
5
5
  import { Icon } from "@poveste/vendors/iconify";
6
6
  //#region src/app/components/tree/StoryGroup.vue?vue&type=script&setup=true&lang.ts
7
7
  var _hoisted_1 = {
8
- "data-test-id": "story-group",
8
+ "data-testid": "story-group",
9
9
  class: "poveste-story-group my-2 first:mt-0 last:mb-0 group"
10
10
  };
11
11
  var _hoisted_2 = { class: "truncate" };
@@ -2,6 +2,6 @@ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export
2
2
  import StoryListFolder_vue_vue_type_script_setup_true_lang_default from "./StoryListFolder.vue_vue_type_script_setup_true_lang.js";
3
3
  /* empty css */
4
4
  //#region src/app/components/tree/StoryListFolder.vue
5
- var StoryListFolder_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryListFolder_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-5f7ae1b5"]]);
5
+ var StoryListFolder_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryListFolder_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-11f8c91e"]]);
6
6
  //#endregion
7
7
  export { StoryListFolder_default as default };
@@ -4,7 +4,7 @@ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock
4
4
  import { Icon } from "@poveste/vendors/iconify";
5
5
  //#region src/app/components/tree/StoryListFolder.vue?vue&type=script&setup=true&lang.ts
6
6
  var _hoisted_1 = {
7
- "data-test-id": "story-list-folder",
7
+ "data-testid": "story-list-folder",
8
8
  class: "poveste-story-list-folder"
9
9
  };
10
10
  var _hoisted_2 = { class: "bind-tree-padding flex items-center gap-2 min-w-0" };
@@ -20,7 +20,7 @@ var StoryListFolder_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
20
20
  depth: { default: 0 }
21
21
  },
22
22
  setup(__props) {
23
- useCssVars((_ctx) => ({ "v52a68e33": folderPadding.value }));
23
+ useCssVars((_ctx) => ({ "v047dbc51": folderPadding.value }));
24
24
  const props = __props;
25
25
  const folderStore = useFolderStore();
26
26
  const folderPath = computed(() => [...props.path, props.folder.title]);
@@ -2,6 +2,6 @@ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export
2
2
  import StoryListItem_vue_vue_type_script_setup_true_lang_default from "./StoryListItem.vue_vue_type_script_setup_true_lang.js";
3
3
  /* empty css */
4
4
  //#region src/app/components/tree/StoryListItem.vue
5
- var StoryListItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryListItem_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-9b200828"]]);
5
+ var StoryListItem_default = /*#__PURE__*/ _plugin_vue_export_helper_default(StoryListItem_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-0fa5d0aa"]]);
6
6
  //#endregion
7
7
  export { StoryListItem_default as default };