@poveste/app 0.15.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.
Files changed (77) hide show
  1. package/dist/app/style/main.pcss +18 -44
  2. package/dist/bundled/App.vue_vue_type_script_setup_true_lang.js +9 -7
  3. package/dist/bundled/app.css +80 -17
  4. package/dist/bundled/components/app/AppActions.vue_vue_type_script_setup_true_lang.js +44 -27
  5. package/dist/bundled/components/app/ShortcutTooltip.js +5 -0
  6. package/dist/bundled/components/app/ShortcutTooltip.vue_vue_type_script_setup_true_lang.js +27 -0
  7. package/dist/bundled/components/base/BaseDropdown.js +6 -0
  8. package/dist/bundled/components/base/BaseDropdown.vue_vue_type_script_setup_true_lang.js +68 -0
  9. package/dist/bundled/components/base/BaseOverflowMenu.vue_vue_type_script_setup_true_lang.js +5 -5
  10. package/dist/bundled/components/base/BaseSelect.js +1 -0
  11. package/dist/bundled/components/base/BaseSelect.vue_vue_type_script_setup_true_lang.js +80 -51
  12. package/dist/bundled/components/base/BaseSplitPane.js +1 -1
  13. package/dist/bundled/components/base/BaseSplitPane.vue_vue_type_script_setup_true_lang.js +65 -4
  14. package/dist/bundled/components/panel/ControlsComponentPropItem.vue_vue_type_script_setup_true_lang.js +15 -13
  15. package/dist/bundled/components/panel/ControlsComponentProps.vue_vue_type_script_setup_true_lang.js +9 -6
  16. package/dist/bundled/components/panel/ControlsComponentState.vue_vue_type_script_setup_true_lang.js +9 -6
  17. package/dist/bundled/components/panel/StatePresets.vue_vue_type_script_setup_true_lang.js +69 -54
  18. package/dist/bundled/components/panel/StoryEvent.vue_vue_type_script_setup_true_lang.js +5 -5
  19. package/dist/bundled/components/panel/StorySidePanel.vue_vue_type_script_setup_true_lang.js +1 -0
  20. package/dist/bundled/components/panel/StorySourceCode.js +1 -1
  21. package/dist/bundled/components/panel/StorySourceCode.vue_vue_type_script_setup_true_lang.js +21 -16
  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 +46 -43
  24. package/dist/bundled/components/story/StoryVariantGridItem.js +1 -1
  25. package/dist/bundled/components/story/StoryVariantGridItem.vue_vue_type_script_setup_true_lang.js +21 -19
  26. package/dist/bundled/components/story/StoryVariantSingle.js +1 -1
  27. package/dist/bundled/components/story/StoryVariantSingle.vue_vue_type_script_setup_true_lang.js +2 -1
  28. package/dist/bundled/components/story/StoryView.vue_vue_type_script_setup_true_lang.js +1 -0
  29. package/dist/bundled/components/toolbar/DevOnlyToolbarOpenInEditor.vue_vue_type_script_setup_true_lang.js +15 -12
  30. package/dist/bundled/components/toolbar/ToolbarBackground.vue_vue_type_script_setup_true_lang.js +20 -16
  31. package/dist/bundled/components/toolbar/ToolbarNewTab.vue_vue_type_script_setup_true_lang.js +16 -13
  32. package/dist/bundled/components/toolbar/ToolbarResponsiveSize.vue_vue_type_script_setup_true_lang.js +35 -24
  33. package/dist/bundled/components/toolbar/ToolbarTextDirection.vue_vue_type_script_setup_true_lang.js +15 -12
  34. package/dist/bundled/index.js +0 -13
  35. package/dist/bundled/sandbox.js +2 -1
  36. package/dist/bundled/util/icons.generated.js +4 -2
  37. package/dist/bundled/util/scroll.js +26 -5
  38. package/dist/bundled/util/state.js +105 -5
  39. package/dist/style.css +731 -357
  40. package/package.json +19 -15
  41. package/src/__tests__/base-select.spec.ts +25 -0
  42. package/src/__tests__/reset.spec.ts +53 -0
  43. package/src/__tests__/state-clone.spec.ts +55 -1
  44. package/src/__tests__/state-types.spec.ts +175 -0
  45. package/src/app/App.vue +43 -34
  46. package/src/app/components/app/AppActions.vue +42 -30
  47. package/src/app/components/app/ShortcutTooltip.vue +29 -0
  48. package/src/app/components/base/BaseDropdown.vue +142 -0
  49. package/src/app/components/base/BaseOverflowMenu.vue +10 -5
  50. package/src/app/components/base/BaseSelect.vue +133 -60
  51. package/src/app/components/base/BaseSplitPane.vue +73 -2
  52. package/src/app/components/panel/ControlsComponentPropItem.vue +18 -17
  53. package/src/app/components/panel/ControlsComponentProps.vue +7 -5
  54. package/src/app/components/panel/ControlsComponentState.vue +7 -5
  55. package/src/app/components/panel/StatePresets.vue +82 -76
  56. package/src/app/components/panel/StoryEvent.vue +10 -9
  57. package/src/app/components/panel/StorySidePanel.vue +1 -0
  58. package/src/app/components/panel/StorySourceCode.vue +47 -41
  59. package/src/app/components/story/StoryResponsivePreview.vue +55 -27
  60. package/src/app/components/story/StoryVariantGridItem.vue +19 -18
  61. package/src/app/components/story/StoryVariantSingle.vue +1 -0
  62. package/src/app/components/story/StoryView.vue +1 -0
  63. package/src/app/components/toolbar/DevOnlyToolbarOpenInEditor.vue +14 -12
  64. package/src/app/components/toolbar/ToolbarBackground.vue +22 -23
  65. package/src/app/components/toolbar/ToolbarNewTab.vue +14 -12
  66. package/src/app/components/toolbar/ToolbarResponsiveSize.vue +31 -25
  67. package/src/app/components/toolbar/ToolbarTextDirection.vue +14 -10
  68. package/src/app/index.ts +0 -17
  69. package/src/app/sandbox.ts +8 -1
  70. package/src/app/style/main.pcss +18 -44
  71. package/src/app/util/icons.generated.ts +2 -2
  72. package/src/app/util/scroll.ts +47 -9
  73. package/src/app/util/state.ts +139 -6
  74. package/dist/app/style/floating.pcss +0 -40
  75. package/dist/bundled/util/tooltip.js +0 -14
  76. package/src/app/style/floating.pcss +0 -40
  77. package/src/app/util/tooltip.ts +0 -13
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import type { Story, Variant } from '../../types'
3
3
  import { Icon } from '@iconify/vue'
4
+ import { HstTooltip } from '@poveste/controls'
4
5
  import { applyState, clone } from '@poveste/shared'
5
6
  import { onClickOutside, useLiveAnnouncer, useStorage, useTimeoutFn } from '@vueuse/core'
6
7
  import { computed, nextTick, onMounted, ref } from 'vue'
@@ -188,81 +189,86 @@ onClickOutside(select, () => stopEditing())
188
189
  @select="id => applyPreset(id)"
189
190
  />
190
191
  </div>
191
- <button
192
- v-tooltip="canEdit ? 'Rename this preset' : null"
193
- type="button"
194
- data-testid="preset-rename"
195
- aria-label="Rename preset"
196
- :disabled="!canEdit"
197
- class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400"
198
- :class="[
199
- canEdit ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
200
- ]"
201
- @click="startEditing()"
202
- >
203
- <Icon
204
- icon="carbon:edit"
205
- class="w-4 h-4"
206
- />
207
- </button>
208
- <button
209
- v-tooltip="canEdit ? 'Delete this preset' : null"
210
- type="button"
211
- data-testid="preset-delete"
212
- aria-label="Delete preset"
213
- :disabled="!canEdit"
214
- class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400"
215
- :class="[
216
- canEdit ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
217
- ]"
218
- @click="deletePreset(selectedOption)"
219
- >
220
- <Icon
221
- icon="carbon:trash-can"
222
- class="w-4 h-4"
223
- />
224
- </button>
225
- <button
226
- v-tooltip="savedNotif ? 'Saved!' : canEdit ? 'Save to preset' : null"
227
- type="button"
228
- data-testid="preset-save"
229
- aria-label="Save to preset"
230
- :disabled="!canEdit"
231
- class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400"
232
- :class="[
233
- canEdit ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
234
- ]"
235
- @click="savePreset()"
236
- >
237
- <Icon
238
- :icon="savedNotif ? 'carbon:checkmark' : 'carbon:save'"
239
- class="w-4 h-4"
240
- />
241
- </button>
242
- <button
243
- v-tooltip="'Create new preset'"
244
- type="button"
245
- data-testid="preset-create"
246
- aria-label="Create new preset"
247
- class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400 opacity-50 hover:opacity-100 focus-visible:opacity-100"
248
- @click="createPreset()"
249
- >
250
- <Icon
251
- icon="carbon:add-alt"
252
- class="w-4 h-4"
253
- />
254
- </button>
255
- <button
256
- v-tooltip="'Reset to initial state'"
257
- type="button"
258
- aria-label="Reset to initial state"
259
- class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400 opacity-50 hover:opacity-100 focus-visible:opacity-100"
260
- @click="resetState()"
261
- >
262
- <Icon
263
- icon="carbon:reset"
264
- class="w-4 h-4"
265
- />
266
- </button>
192
+ <HstTooltip :content="canEdit ? 'Rename this preset' : null">
193
+ <button
194
+ type="button"
195
+ data-testid="preset-rename"
196
+ aria-label="Rename preset"
197
+ :disabled="!canEdit"
198
+ class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400"
199
+ :class="[
200
+ canEdit ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
201
+ ]"
202
+ @click="startEditing()"
203
+ >
204
+ <Icon
205
+ icon="carbon:edit"
206
+ class="w-4 h-4"
207
+ />
208
+ </button>
209
+ </HstTooltip>
210
+ <HstTooltip :content="canEdit ? 'Delete this preset' : null">
211
+ <button
212
+ type="button"
213
+ data-testid="preset-delete"
214
+ aria-label="Delete preset"
215
+ :disabled="!canEdit"
216
+ class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400"
217
+ :class="[
218
+ canEdit ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
219
+ ]"
220
+ @click="deletePreset(selectedOption)"
221
+ >
222
+ <Icon
223
+ icon="carbon:trash-can"
224
+ class="w-4 h-4"
225
+ />
226
+ </button>
227
+ </HstTooltip>
228
+ <HstTooltip :content="savedNotif ? 'Saved!' : canEdit ? 'Save to preset' : null">
229
+ <button
230
+ type="button"
231
+ data-testid="preset-save"
232
+ aria-label="Save to preset"
233
+ :disabled="!canEdit"
234
+ class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400"
235
+ :class="[
236
+ canEdit ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
237
+ ]"
238
+ @click="savePreset()"
239
+ >
240
+ <Icon
241
+ :icon="savedNotif ? 'carbon:checkmark' : 'carbon:save'"
242
+ class="w-4 h-4"
243
+ />
244
+ </button>
245
+ </HstTooltip>
246
+ <HstTooltip content="Create new preset">
247
+ <button
248
+ type="button"
249
+ data-testid="preset-create"
250
+ aria-label="Create new preset"
251
+ class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400 opacity-50 hover:opacity-100 focus-visible:opacity-100"
252
+ @click="createPreset()"
253
+ >
254
+ <Icon
255
+ icon="carbon:add-alt"
256
+ class="w-4 h-4"
257
+ />
258
+ </button>
259
+ </HstTooltip>
260
+ <HstTooltip content="Reset to initial state">
261
+ <button
262
+ type="button"
263
+ aria-label="Reset to initial state"
264
+ class="flex-none flex p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400 opacity-50 hover:opacity-100 focus-visible:opacity-100"
265
+ @click="resetState()"
266
+ >
267
+ <Icon
268
+ icon="carbon:reset"
269
+ class="w-4 h-4"
270
+ />
271
+ </button>
272
+ </HstTooltip>
267
273
  </div>
268
274
  </template>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import type { HstEvent } from '../../stores/events'
3
3
  import { computed } from 'vue'
4
+ import BaseDropdown from '../base/BaseDropdown.vue'
4
5
 
5
6
  const props = defineProps<{
6
7
  event: HstEvent
@@ -24,21 +25,21 @@ const formattedArgument = computed(() => {
24
25
  </script>
25
26
 
26
27
  <template>
27
- <VDropdown
28
+ <BaseDropdown
29
+ side="right"
28
30
  class="poveste-story-event group"
29
- placement="right"
30
31
  data-testid="event-item"
31
32
  >
32
- <template #default="{ shown }">
33
- <div
34
- class="group-hover:bg-primary-100 dark:group-hover:bg-primary-700 cursor-pointer py-2 px-4 flex items-baseline gap-1 leading-normal"
33
+ <template #default="{ open }">
34
+ <button
35
+ class="group-hover:bg-primary-100 dark:group-hover:bg-primary-700 cursor-pointer py-2 px-4 w-full text-left flex items-baseline gap-1 leading-normal"
35
36
  :class="[
36
- shown ? 'bg-primary-50 dark:bg-primary-600' : 'group-odd:bg-gray-100/50 dark:group-odd:bg-gray-750/40',
37
+ open ? 'bg-primary-50 dark:bg-primary-600' : 'group-odd:bg-gray-100/50 dark:group-odd:bg-gray-750/40',
37
38
  ]"
38
39
  >
39
40
  <span
40
41
  :class="{
41
- 'text-primary-500': shown,
42
+ 'text-primary-500': open,
42
43
  }"
43
44
  >
44
45
  {{ event.name }}
@@ -47,7 +48,7 @@ const formattedArgument = computed(() => {
47
48
  v-if="event.argument"
48
49
  class="text-xs opacity-50 truncate"
49
50
  >{{ formattedArgument }}</span>
50
- </div>
51
+ </button>
51
52
  </template>
52
53
 
53
54
  <template #popper>
@@ -55,5 +56,5 @@ const formattedArgument = computed(() => {
55
56
  <pre class="p-4">{{ event.argument }}</pre>
56
57
  </div>
57
58
  </template>
58
- </VDropdown>
59
+ </BaseDropdown>
59
60
  </template>
@@ -53,6 +53,7 @@ const panelContentComponent = computed(() => {
53
53
  <BaseSplitPane
54
54
  v-else
55
55
  :save-id="`story-sidepane-${innerOrientation}`"
56
+ label="Resize the controls panel"
56
57
  :orientation="innerOrientation"
57
58
  class="poveste-story-side-panel poveste-loaded h-full"
58
59
  data-testid="story-side-panel"
@@ -2,7 +2,7 @@
2
2
  import type { HighlighterCore } from 'shiki/core'
3
3
  import type { Story, Variant } from '../../types'
4
4
  import { Icon } from '@iconify/vue'
5
- import { HstCopyIcon } from '@poveste/controls'
5
+ import { HstCopyIcon, HstTooltip } from '@poveste/controls'
6
6
  import { unindent } from '@poveste/shared'
7
7
  import { clientSupportPlugins } from 'virtual:$poveste-support-plugins-client'
8
8
  import { computed, markRaw, nextTick, onMounted, ref, shallowRef, watch, watchEffect } from 'vue'
@@ -151,50 +151,56 @@ watch(sourceHtml, async () => {
151
151
 
152
152
  <!-- Display source modes -->
153
153
  <div class="flex flex-none gap-px h-full py-2">
154
- <button
155
- v-tooltip="!dynamicSourceCode ? 'Dynamic source code is not available' : displayedSource !== 'dynamic' ? 'Switch to dynamic source' : null"
156
- class="flex items-center gap-1 h-full px-1 bg-gray-500/10 rounded-l transition-all ease-[cubic-bezier(0,1,.6,1)] duration-300 overflow-hidden"
157
- :class="[
158
- displayedSource !== 'dynamic' ? 'max-w-6 opacity-70' : 'max-w-[82px] text-primary-800 dark:text-primary-400',
159
- dynamicSourceCode ? 'cursor-pointer hover:bg-gray-500/30 active:bg-gray-600/50' : displayedSource !== 'dynamic' && 'opacity-50',
160
- ]"
161
- @click="dynamicSourceCode && (displayedSource = 'dynamic')"
154
+ <HstTooltip
155
+ :content="!dynamicSourceCode ? 'Dynamic source code is not available' : displayedSource !== 'dynamic' ? 'Switch to dynamic source' : null"
162
156
  >
163
- <Icon
164
- icon="carbon:flash"
165
- class="w-4 h-4 flex-none"
166
- />
167
- <span
168
- class="transition-opacity duration-300"
169
- :class="{
170
- 'opacity-0': displayedSource !== 'dynamic',
171
- }"
157
+ <button
158
+ class="flex items-center gap-1 h-full px-1 bg-gray-500/10 rounded-l transition-all ease-[cubic-bezier(0,1,.6,1)] duration-300 overflow-hidden"
159
+ :class="[
160
+ displayedSource !== 'dynamic' ? 'max-w-6 opacity-70' : 'max-w-[82px] text-primary-800 dark:text-primary-400',
161
+ dynamicSourceCode ? 'cursor-pointer hover:bg-gray-500/30 active:bg-gray-600/50' : displayedSource !== 'dynamic' && 'opacity-50',
162
+ ]"
163
+ @click="dynamicSourceCode && (displayedSource = 'dynamic')"
172
164
  >
173
- Dynamic
174
- </span>
175
- </button>
176
- <button
177
- v-tooltip="!staticSourceCode ? 'Static source code is not available' : displayedSource !== 'static' ? 'Switch to static source' : null"
178
- class="flex items-center gap-1 h-full px-1 bg-gray-500/10 rounded-r transition-all ease-[cubic-bezier(0,1,.6,1)] duration-300 overflow-hidden"
179
- :class="[
180
- displayedSource !== 'static' ? 'max-w-6 opacity-70' : 'max-w-[63px] text-primary-800 dark:text-primary-400',
181
- staticSourceCode ? 'cursor-pointer hover:bg-gray-500/30 active:bg-gray-600/50' : displayedSource !== 'static' && 'opacity-50',
182
- ]"
183
- @click="staticSourceCode && (displayedSource = 'static')"
165
+ <Icon
166
+ icon="carbon:flash"
167
+ class="w-4 h-4 flex-none"
168
+ />
169
+ <span
170
+ class="transition-opacity duration-300"
171
+ :class="{
172
+ 'opacity-0': displayedSource !== 'dynamic',
173
+ }"
174
+ >
175
+ Dynamic
176
+ </span>
177
+ </button>
178
+ </HstTooltip>
179
+ <HstTooltip
180
+ :content="!staticSourceCode ? 'Static source code is not available' : displayedSource !== 'static' ? 'Switch to static source' : null"
184
181
  >
185
- <Icon
186
- icon="carbon:document"
187
- class="w-4 h-4 flex-none"
188
- />
189
- <span
190
- class="transition-opacity duration-300"
191
- :class="{
192
- 'opacity-0': displayedSource !== 'static',
193
- }"
182
+ <button
183
+ class="flex items-center gap-1 h-full px-1 bg-gray-500/10 rounded-r transition-all ease-[cubic-bezier(0,1,.6,1)] duration-300 overflow-hidden"
184
+ :class="[
185
+ displayedSource !== 'static' ? 'max-w-6 opacity-70' : 'max-w-[63px] text-primary-800 dark:text-primary-400',
186
+ staticSourceCode ? 'cursor-pointer hover:bg-gray-500/30 active:bg-gray-600/50' : displayedSource !== 'static' && 'opacity-50',
187
+ ]"
188
+ @click="staticSourceCode && (displayedSource = 'static')"
194
189
  >
195
- Static
196
- </span>
197
- </button>
190
+ <Icon
191
+ icon="carbon:document"
192
+ class="w-4 h-4 flex-none"
193
+ />
194
+ <span
195
+ class="transition-opacity duration-300"
196
+ :class="{
197
+ 'opacity-0': displayedSource !== 'static',
198
+ }"
199
+ >
200
+ Static
201
+ </span>
202
+ </button>
203
+ </HstTooltip>
198
204
  </div>
199
205
 
200
206
  <HstCopyIcon
@@ -2,8 +2,8 @@
2
2
  import type { Ref } from 'vue'
3
3
  import type { Variant } from '../../types'
4
4
  import { Icon } from '@iconify/vue'
5
+ import { HstTooltip } from '@poveste/controls'
5
6
  import { useEventListener } from '@vueuse/core'
6
- import { VTooltip as vTooltip } from 'floating-vue'
7
7
  import { computed, onUnmounted, ref } from 'vue'
8
8
  import { usePreviewSettingsStore } from '../../stores/preview-settings'
9
9
  import CheckerboardPattern from '../misc/CheckerboardPattern.vue'
@@ -201,9 +201,18 @@ const sizeTooltip = computed(() => `${responsiveWidth.value ?? 'Auto'} × ${resp
201
201
  preview would otherwise be shown against the bare box below the
202
202
  first screenful. The checkerboard is measured against this for the
203
203
  same reason — but the draggers are not, since they belong to the
204
- visible box rather than to the story's full height. -->
204
+ visible box rather than to the story's full height.
205
+ `flex`, and `flex-1` rather than `h-full` below, because that is the
206
+ half `min-h-full` costs: a percentage height resolves against a
207
+ parent's *specified* height, and this one's is `auto`. So `h-full`
208
+ collapsed all the way down to the iframe, which then fell back to
209
+ its own default of 150px and clipped every story taller than that
210
+ (#949). A flex child reads the used height instead, which
211
+ `min-height` does give it — and with `min-height: auto` it still
212
+ refuses to shrink below its content, so a tall story pushes this box
213
+ taller exactly as it did. -->
205
214
  <div
206
- class="rounded-lg min-h-full relative"
215
+ class="rounded-lg min-h-full relative flex flex-col"
207
216
  :class="responsive ? 'bind-preview-bg' : ''"
208
217
  :data-testid="responsive ? 'responsive-preview-bg' : undefined"
209
218
  >
@@ -212,10 +221,17 @@ const sizeTooltip = computed(() => `${responsiveWidth.value ?? 'Auto'} × ${resp
212
221
  class="absolute inset-0 w-full h-full text-gray-500/20"
213
222
  />
214
223
  <div
215
- class="h-full relative"
224
+ class="flex-1 relative flex flex-col"
216
225
  :class="responsive ? 'p-8' : ''"
217
226
  >
218
- <div class="w-full h-full relative">
227
+ <!-- `grid`, so the one in-flow child stretches to this box without
228
+ asking for a percentage of it. `h-full` here read a height that
229
+ is decided by flex, which Chrome treats as indefinite for
230
+ percentage resolution — the same wall one level up. Stretching
231
+ is not shrinking: the child keeps its `min-height: auto`, so an
232
+ inline story taller than the preview still grows this and the
233
+ box above it (#258). -->
234
+ <div class="w-full flex-1 relative grid">
219
235
  <div class="absolute inset-0" />
220
236
 
221
237
  <slot
@@ -243,31 +259,43 @@ const sizeTooltip = computed(() => `${responsiveWidth.value ?? 'Auto'} × ${resp
243
259
 
244
260
  <!-- Resize Dragger -->
245
261
  <template v-if="isResponsiveEnabled">
246
- <div
247
- ref="horizontalDragger"
248
- v-tooltip.right="sizeTooltip"
249
- class="absolute w-4 top-0 bottom-4 right-0 hover:bg-primary-500/30 flex items-center justify-center cursor-ew-resize group hover:text-primary-500"
262
+ <HstTooltip
263
+ :content="sizeTooltip"
264
+ side="right"
250
265
  >
251
- <Icon
252
- icon="mdi:drag-vertical-variant"
253
- class="w-4 h-4 opacity-20 group-hover:opacity-90"
254
- />
255
- </div>
256
- <div
257
- ref="verticalDragger"
258
- v-tooltip.bottom="sizeTooltip"
259
- class="absolute h-4 left-0 right-4 bottom-0 hover:bg-primary-500/30 flex items-center justify-center cursor-ns-resize group hover:text-primary-500"
266
+ <div
267
+ ref="horizontalDragger"
268
+ class="absolute w-4 top-0 bottom-4 right-0 hover:bg-primary-500/30 flex items-center justify-center cursor-ew-resize group hover:text-primary-500"
269
+ >
270
+ <Icon
271
+ icon="mdi:drag-vertical-variant"
272
+ class="w-4 h-4 opacity-20 group-hover:opacity-90"
273
+ />
274
+ </div>
275
+ </HstTooltip>
276
+ <HstTooltip
277
+ :content="sizeTooltip"
278
+ side="bottom"
260
279
  >
261
- <Icon
262
- icon="mdi:drag-horizontal-variant"
263
- class="w-4 h-4 opacity-20 group-hover:opacity-90"
280
+ <div
281
+ ref="verticalDragger"
282
+ class="absolute h-4 left-0 right-4 bottom-0 hover:bg-primary-500/30 flex items-center justify-center cursor-ns-resize group hover:text-primary-500"
283
+ >
284
+ <Icon
285
+ icon="mdi:drag-horizontal-variant"
286
+ class="w-4 h-4 opacity-20 group-hover:opacity-90"
287
+ />
288
+ </div>
289
+ </HstTooltip>
290
+ <HstTooltip
291
+ :content="sizeTooltip"
292
+ side="bottom"
293
+ >
294
+ <div
295
+ ref="cornerDragger"
296
+ class="absolute w-4 h-4 right-0 bottom-0 hover:bg-primary-500/30 flex items-center justify-center cursor-nwse-resize group hover:text-primary-500"
264
297
  />
265
- </div>
266
- <div
267
- ref="cornerDragger"
268
- v-tooltip.bottom="sizeTooltip"
269
- class="absolute w-4 h-4 right-0 bottom-0 hover:bg-primary-500/30 flex items-center justify-center cursor-nwse-resize group hover:text-primary-500"
270
- />
298
+ </HstTooltip>
271
299
  </template>
272
300
  </div>
273
301
  </div>
@@ -2,7 +2,7 @@
2
2
  import type { PropType } from 'vue'
3
3
  import type { Story, Variant } from '../../types'
4
4
  import { Icon } from '@iconify/vue'
5
- import { HstCopyIcon } from '@poveste/controls'
5
+ import { HstCopyIcon, HstTooltip } from '@poveste/controls'
6
6
  import { useResizeObserver } from '@vueuse/core'
7
7
  import { computed, nextTick, ref, toRefs, watch } from 'vue'
8
8
  import { useRouter } from 'vue-router'
@@ -111,25 +111,26 @@ const useIframe = computed(() => {
111
111
  >
112
112
  <!-- Header -->
113
113
  <div class="flex-none flex items-center">
114
- <RouterLink
115
- v-tooltip="variant.title"
116
- :to="targetRoute"
117
- class="rounded w-max px-2 py-0.5 min-w-16 cursor-pointer flex items-center gap-1 flex-shrink"
118
- :class="{
119
- 'hover:bg-gray-200 text-gray-500 dark:hover:bg-gray-800': !isActive,
120
- 'bg-primary-200 hover:bg-primary-300 text-primary-800 dark:bg-primary-700 dark:hover:bg-primary-800 dark:text-primary-200': isActive,
121
- }"
122
- >
123
- <Icon
124
- :icon="variant.icon ?? 'carbon:cube'"
125
- class="w-4 h-4 opacity-50"
114
+ <HstTooltip :content="variant.title">
115
+ <RouterLink
116
+ :to="targetRoute"
117
+ class="rounded w-max px-2 py-0.5 min-w-16 cursor-pointer flex items-center gap-1 flex-shrink"
126
118
  :class="{
127
- 'text-gray-500': !isActive && !variant.iconColor,
128
- 'bind-icon-color': !isActive && variant.iconColor,
119
+ 'hover:bg-gray-200 text-gray-500 dark:hover:bg-gray-800': !isActive,
120
+ 'bg-primary-200 hover:bg-primary-300 text-primary-800 dark:bg-primary-700 dark:hover:bg-primary-800 dark:text-primary-200': isActive,
129
121
  }"
130
- />
131
- <span class="truncate flex-1">{{ variant.title }}</span>
132
- </RouterLink>
122
+ >
123
+ <Icon
124
+ :icon="variant.icon ?? 'carbon:cube'"
125
+ class="w-4 h-4 opacity-50"
126
+ :class="{
127
+ 'text-gray-500': !isActive && !variant.iconColor,
128
+ 'bind-icon-color': !isActive && variant.iconColor,
129
+ }"
130
+ />
131
+ <span class="truncate flex-1">{{ variant.title }}</span>
132
+ </RouterLink>
133
+ </HstTooltip>
133
134
 
134
135
  <!-- Toolbar -->
135
136
  <div class="flex-none ml-auto hidden group-hover:flex group-focus-within:flex items-center">
@@ -36,6 +36,7 @@ const showsPreview = computed(() => !!variant.value || autoSelectsVariant(storyS
36
36
  -->
37
37
  <BaseSplitPane
38
38
  save-id="story-single-main-split"
39
+ label="Resize the variant list"
39
40
  :min="5"
40
41
  :max="40"
41
42
  :default-split="17"
@@ -111,6 +111,7 @@ function scrollDocsToTop() {
111
111
  <BaseSplitPane
112
112
  v-else
113
113
  :save-id="`story-main-${placement}`"
114
+ label="Resize the preview"
114
115
  :orientation="splitOrientation"
115
116
  :min="30"
116
117
  :max="95"
@@ -1,5 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import { Icon } from '@iconify/vue'
3
+ import { HstTooltip } from '@poveste/controls'
3
4
  import { openInEditor } from '../../util/open-in-editor.js'
4
5
  import TopBarChipItem from '../app/TopBarChipItem.vue'
5
6
 
@@ -10,16 +11,17 @@ defineProps<{
10
11
  </script>
11
12
 
12
13
  <template>
13
- <TopBarChipItem
14
- v-tooltip="tooltip"
15
- aria-label="Open story in editor"
16
- target="_blank"
17
- class="poveste-toolbar-open-in-editor"
18
- @click="openInEditor(file)"
19
- >
20
- <Icon
21
- icon="carbon:script-reference"
22
- class="w-4 h-4"
23
- />
24
- </TopBarChipItem>
14
+ <HstTooltip :content="tooltip">
15
+ <TopBarChipItem
16
+ aria-label="Open story in editor"
17
+ target="_blank"
18
+ class="poveste-toolbar-open-in-editor"
19
+ @click="openInEditor(file)"
20
+ >
21
+ <Icon
22
+ icon="carbon:script-reference"
23
+ class="w-4 h-4"
24
+ />
25
+ </TopBarChipItem>
26
+ </HstTooltip>
25
27
  </template>
@@ -4,6 +4,7 @@ import { Icon } from '@iconify/vue'
4
4
  import { usePreviewSettingsStore } from '../../stores/preview-settings'
5
5
  import { povesteConfig } from '../../util/config'
6
6
  import BaseCheckbox from '../base/BaseCheckbox.vue'
7
+ import BaseDropdown from '../base/BaseDropdown.vue'
7
8
 
8
9
  const previewSettings = usePreviewSettingsStore()
9
10
  const settings = previewSettings.currentSettings
@@ -21,32 +22,30 @@ const showDropdown = showColorScheme || !!povesteConfig.backgroundPresets?.lengt
21
22
  </script>
22
23
 
23
24
  <template>
24
- <VDropdown
25
+ <BaseDropdown
25
26
  v-if="showDropdown"
26
- placement="bottom-end"
27
- :skidding="6"
27
+ side="bottom"
28
+ align="end"
29
+ :align-offset="6"
30
+ tooltip="Preview appearance"
28
31
  class="poveste-toolbar-background flex-none"
29
32
  data-testid="toolbar-background"
30
33
  >
31
- <!-- The tooltip would sit on top of the first row of the dropdown, so drop
32
- it while the dropdown is open. -->
33
- <template #default="{ shown }">
34
- <div
35
- v-tooltip="shown ? '' : 'Preview appearance'"
36
- 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"
37
- >
38
- <Icon
39
- icon="carbon:color-palette"
40
- class="w-4 h-4"
41
- />
42
- <Icon
43
- icon="carbon:chevron-down"
44
- class="w-3 h-3 opacity-40 group-hover:opacity-70"
45
- />
46
- </div>
47
- </template>
34
+ <button
35
+ 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"
36
+ aria-label="Preview appearance"
37
+ >
38
+ <Icon
39
+ icon="carbon:color-palette"
40
+ class="w-4 h-4"
41
+ />
42
+ <Icon
43
+ icon="carbon:chevron-down"
44
+ class="w-3 h-3 opacity-40 group-hover:opacity-70"
45
+ />
46
+ </button>
48
47
 
49
- <template #popper="{ hide }">
48
+ <template #popper="{ close }">
50
49
  <div
51
50
  class="flex flex-col items-stretch"
52
51
  data-testid="background-popper"
@@ -89,7 +88,7 @@ const showDropdown = showColorScheme || !!povesteConfig.backgroundPresets?.lengt
89
88
  ? 'bg-primary-500 hover:bg-primary-600 text-white dark:text-black'
90
89
  : 'bg-transparent hover:bg-primary-100 dark:hover:bg-primary-700',
91
90
  ]"
92
- @click="previewSettings.setBackgroundColor(option.color);hide()"
91
+ @click="previewSettings.setBackgroundColor(option.color);close()"
93
92
  >
94
93
  <span class="mr-auto">{{ option.label }}</span>
95
94
  <template v-if="option.color !== '$checkerboard'">
@@ -107,5 +106,5 @@ const showDropdown = showColorScheme || !!povesteConfig.backgroundPresets?.lengt
107
106
  </button>
108
107
  </div>
109
108
  </template>
110
- </VDropdown>
109
+ </BaseDropdown>
111
110
  </template>