@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed, onUnmounted, ref, watch } from 'vue'
|
|
2
|
+
import { computed, onUnmounted, ref, useId, watch } from 'vue'
|
|
3
3
|
|
|
4
4
|
const props = defineProps({
|
|
5
5
|
orientation: {
|
|
@@ -49,6 +49,17 @@ const props = defineProps({
|
|
|
49
49
|
default: true,
|
|
50
50
|
},
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* What the divider is called to a screen reader.
|
|
54
|
+
*
|
|
55
|
+
* Four are on screen at once and they resize different things, so a shared
|
|
56
|
+
* default would announce four identical separators.
|
|
57
|
+
*/
|
|
58
|
+
label: {
|
|
59
|
+
type: String,
|
|
60
|
+
default: 'Resize panels',
|
|
61
|
+
},
|
|
62
|
+
|
|
52
63
|
/**
|
|
53
64
|
* Whether the first pane exists at all.
|
|
54
65
|
*
|
|
@@ -176,7 +187,7 @@ let startPosition = 0
|
|
|
176
187
|
let startSplit = 0
|
|
177
188
|
const el = ref<HTMLElement>()
|
|
178
189
|
|
|
179
|
-
function dragStart(e) {
|
|
190
|
+
function dragStart(e: MouseEvent) {
|
|
180
191
|
dragging.value = true
|
|
181
192
|
startPosition = props.orientation === 'landscape' ? e.pageX : e.pageY
|
|
182
193
|
startSplit = boundSplit.value
|
|
@@ -184,7 +195,7 @@ function dragStart(e) {
|
|
|
184
195
|
window.addEventListener('mouseup', dragEnd)
|
|
185
196
|
}
|
|
186
197
|
|
|
187
|
-
function dragMove(e) {
|
|
198
|
+
function dragMove(e: MouseEvent) {
|
|
188
199
|
if (dragging.value) {
|
|
189
200
|
// The move listener is on `window`, so it can outlive the element for the
|
|
190
201
|
// rest of a drag if the pane unmounts under it.
|
|
@@ -225,6 +236,56 @@ watch(hasBothPanes, (both) => {
|
|
|
225
236
|
}
|
|
226
237
|
})
|
|
227
238
|
|
|
239
|
+
/*
|
|
240
|
+
* The keyboard half, which the divider had none of: it was a `div` with a
|
|
241
|
+
* `mousedown` handler, so the layout could only be changed with a pointer and a
|
|
242
|
+
* screen reader was told nothing was there (#995).
|
|
243
|
+
*
|
|
244
|
+
* The arrow step is one unit of whatever `split` is measured in — percent
|
|
245
|
+
* normally, pixels under `fixed` — and `PageUp`/`PageDown` move by ten, because
|
|
246
|
+
* one pixel per press is not a usable way to move a pixel-sized pane.
|
|
247
|
+
*/
|
|
248
|
+
const STEP = 1
|
|
249
|
+
const PAGE_STEP = 10
|
|
250
|
+
|
|
251
|
+
function moveBy(amount: number) {
|
|
252
|
+
currentSplit.value = Math.min(props.max, Math.max(props.min, boundSplit.value + amount))
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function onKeydown(event: KeyboardEvent) {
|
|
256
|
+
const back = props.orientation === 'landscape' ? 'ArrowLeft' : 'ArrowUp'
|
|
257
|
+
const forward = props.orientation === 'landscape' ? 'ArrowRight' : 'ArrowDown'
|
|
258
|
+
|
|
259
|
+
switch (event.key) {
|
|
260
|
+
case back:
|
|
261
|
+
moveBy(-STEP)
|
|
262
|
+
break
|
|
263
|
+
case forward:
|
|
264
|
+
moveBy(STEP)
|
|
265
|
+
break
|
|
266
|
+
case 'PageUp':
|
|
267
|
+
moveBy(-PAGE_STEP)
|
|
268
|
+
break
|
|
269
|
+
case 'PageDown':
|
|
270
|
+
moveBy(PAGE_STEP)
|
|
271
|
+
break
|
|
272
|
+
case 'Home':
|
|
273
|
+
currentSplit.value = props.min
|
|
274
|
+
break
|
|
275
|
+
case 'End':
|
|
276
|
+
currentSplit.value = props.max
|
|
277
|
+
break
|
|
278
|
+
default:
|
|
279
|
+
return
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Only for a key this handles: the arrows scroll the pane otherwise, and a
|
|
283
|
+
// blanket `prevent` would stop that everywhere the divider has focus.
|
|
284
|
+
event.preventDefault()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const firstPaneId = useId()
|
|
288
|
+
|
|
228
289
|
function removeDragListeners() {
|
|
229
290
|
window.removeEventListener('mousemove', dragMove)
|
|
230
291
|
window.removeEventListener('mouseup', dragEnd)
|
|
@@ -248,6 +309,7 @@ onUnmounted(() => {
|
|
|
248
309
|
>
|
|
249
310
|
<div
|
|
250
311
|
v-if="showFirst"
|
|
312
|
+
:id="firstPaneId"
|
|
251
313
|
class="relative top-0 left-0 z-20"
|
|
252
314
|
:class="{
|
|
253
315
|
'pointer-events-none': dragging,
|
|
@@ -260,7 +322,15 @@ onUnmounted(() => {
|
|
|
260
322
|
|
|
261
323
|
<div
|
|
262
324
|
v-if="hasBothPanes"
|
|
263
|
-
|
|
325
|
+
role="separator"
|
|
326
|
+
tabindex="0"
|
|
327
|
+
:aria-label="label"
|
|
328
|
+
:aria-orientation="orientation === 'landscape' ? 'vertical' : 'horizontal'"
|
|
329
|
+
:aria-controls="firstPaneId"
|
|
330
|
+
:aria-valuenow="Math.round(boundSplit)"
|
|
331
|
+
:aria-valuemin="min"
|
|
332
|
+
:aria-valuemax="max"
|
|
333
|
+
class="dragger absolute z-100 hover:bg-primary-500/50 focus-visible:bg-primary-500/50 focus-visible:outline-2 focus-visible:outline-primary-500 transition-colors duration-150 delay-150"
|
|
264
334
|
:class="{
|
|
265
335
|
'top-0 bottom-0 cursor-ew-resize': orientation === 'landscape',
|
|
266
336
|
'left-0 right-0 cursor-ns-resize': orientation === 'portrait',
|
|
@@ -268,6 +338,7 @@ onUnmounted(() => {
|
|
|
268
338
|
'bg-primary-500/25': dragging,
|
|
269
339
|
}"
|
|
270
340
|
@mousedown.prevent="dragStart"
|
|
341
|
+
@keydown="onKeydown"
|
|
271
342
|
/>
|
|
272
343
|
</div>
|
|
273
344
|
<div
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { AutoPropComponentDefinition, PropDefinition } from '@poveste/shared'
|
|
3
3
|
import type { Variant } from '../../types'
|
|
4
4
|
import { Icon } from '@iconify/vue'
|
|
5
|
-
import { HstCheckbox, HstJson, HstNumber, HstText } from '@poveste/controls'
|
|
5
|
+
import { HstCheckbox, HstJson, HstNumber, HstText, HstTooltip } from '@poveste/controls'
|
|
6
6
|
import { computed } from 'vue'
|
|
7
7
|
|
|
8
8
|
const props = defineProps<{
|
|
@@ -50,24 +50,36 @@ const canReset = computed(() => props.variant.state?._hPropState?.[props.compone
|
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
52
|
<template>
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
<!-- The remove action is beside the control, not in its actions slot. Inside
|
|
54
|
+
the control's <label> or checkbox, a label forwarded clicks to it, its
|
|
55
|
+
name joined the control's name, and Space on it toggled the checkbox (#827). -->
|
|
56
|
+
<div
|
|
55
57
|
v-if="comp"
|
|
56
|
-
|
|
57
|
-
:placeholder="model === undefined ? definition?.default : null"
|
|
58
|
-
class="poveste-controls-component-prop-item"
|
|
59
|
-
:title="`${definition.name}${canReset ? ' *' : ''}`"
|
|
58
|
+
class="flex items-end hover:bg-primary-100 dark:hover:bg-primary-800"
|
|
60
59
|
>
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
<component
|
|
61
|
+
:is="comp"
|
|
62
|
+
v-model="model"
|
|
63
|
+
:placeholder="model === undefined ? definition?.default : null"
|
|
64
|
+
class="poveste-controls-component-prop-item grow min-w-0"
|
|
65
|
+
:title="`${definition.name}${canReset ? ' *' : ''}`"
|
|
66
|
+
/>
|
|
67
|
+
<HstTooltip content="Remove override">
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
:aria-label="`Remove override of ${definition.name}`"
|
|
71
|
+
:disabled="!canReset"
|
|
72
|
+
class="flex-none flex mr-2 mb-[10px] p-0 bg-transparent border-0 cursor-pointer text-gray-900 dark:text-gray-100 hover:text-primary-500 dark:hover:text-primary-400"
|
|
66
73
|
:class="[
|
|
67
|
-
canReset ? 'opacity-50 hover:opacity-100' : 'opacity-25 pointer-events-none',
|
|
74
|
+
canReset ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
|
|
68
75
|
]"
|
|
69
|
-
@click
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
@click="reset()"
|
|
77
|
+
>
|
|
78
|
+
<Icon
|
|
79
|
+
icon="carbon:erase"
|
|
80
|
+
class="w-4 h-4"
|
|
81
|
+
/>
|
|
82
|
+
</button>
|
|
83
|
+
</HstTooltip>
|
|
84
|
+
</div>
|
|
73
85
|
</template>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { AutoPropComponentDefinition } from '@poveste/shared'
|
|
3
3
|
import type { Variant } from '../../types'
|
|
4
4
|
import { Icon } from '@iconify/vue'
|
|
5
|
+
import { HstTooltip } from '@poveste/controls'
|
|
5
6
|
import ControlsComponentPropItem from './ControlsComponentPropItem.vue'
|
|
6
7
|
|
|
7
8
|
defineProps<{
|
|
@@ -13,11 +14,12 @@ defineProps<{
|
|
|
13
14
|
<template>
|
|
14
15
|
<div class="poveste-controls-component-props">
|
|
15
16
|
<div class="font-mono p-2 flex items-center gap-1">
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
<HstTooltip content="Auto-detected props">
|
|
18
|
+
<Icon
|
|
19
|
+
icon="carbon:flash"
|
|
20
|
+
class="w-4 h-4 text-primary-500 flex-none"
|
|
21
|
+
/>
|
|
22
|
+
</HstTooltip>
|
|
21
23
|
<div>
|
|
22
24
|
<span class="opacity-30"><</span>{{ definition.name }}<span class="opacity-30">></span>
|
|
23
25
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Variant } from '@poveste/shared'
|
|
3
3
|
import { Icon } from '@iconify/vue'
|
|
4
|
+
import { HstTooltip } from '@poveste/controls'
|
|
4
5
|
import { computed } from 'vue'
|
|
5
6
|
import ControlsComponentStateItem from './ControlsComponentStateItem.vue'
|
|
6
7
|
|
|
@@ -15,11 +16,12 @@ const stateKeys = computed(() => Object.keys(props.variant.state || {})
|
|
|
15
16
|
<template>
|
|
16
17
|
<div class="poveste-controls-component-init-state">
|
|
17
18
|
<div class="p-2 flex items-center gap-1">
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
<HstTooltip content="Auto-detected state">
|
|
20
|
+
<Icon
|
|
21
|
+
icon="carbon:data-vis-1"
|
|
22
|
+
class="w-4 h-4 text-primary-500 flex-none"
|
|
23
|
+
/>
|
|
24
|
+
</HstTooltip>
|
|
23
25
|
<div>
|
|
24
26
|
State
|
|
25
27
|
</div>
|
|
@@ -25,13 +25,13 @@ const hasEvents = computed(() => eventsStore.events.length)
|
|
|
25
25
|
<BaseOverflowMenu class="poveste-pane-tabs h-10 flex-none border-b border-gray-100 dark:border-gray-750">
|
|
26
26
|
<BaseTab
|
|
27
27
|
:to="{ ...$route, query: { ...$route.query, tab: '' } }"
|
|
28
|
-
:matched="!$route.query
|
|
28
|
+
:matched="!$route.query['tab']"
|
|
29
29
|
>
|
|
30
30
|
Controls
|
|
31
31
|
</BaseTab>
|
|
32
32
|
<BaseTab
|
|
33
33
|
:to="{ ...$route, query: { ...$route.query, tab: 'docs' } }"
|
|
34
|
-
:matched="$route.query
|
|
34
|
+
:matched="$route.query['tab'] === 'docs'"
|
|
35
35
|
:class="{
|
|
36
36
|
'opacity-70': !renderedDoc,
|
|
37
37
|
}"
|
|
@@ -40,7 +40,7 @@ const hasEvents = computed(() => eventsStore.events.length)
|
|
|
40
40
|
</BaseTab>
|
|
41
41
|
<BaseTab
|
|
42
42
|
:to="{ ...$route, query: { ...$route.query, tab: 'events' } }"
|
|
43
|
-
:matched="$route.query
|
|
43
|
+
:matched="$route.query['tab'] === 'events'"
|
|
44
44
|
:class="{
|
|
45
45
|
'opacity-70': !hasEvents,
|
|
46
46
|
}"
|
|
@@ -54,13 +54,13 @@ const hasEvents = computed(() => eventsStore.events.length)
|
|
|
54
54
|
<template #overflow>
|
|
55
55
|
<BaseOverflowTab
|
|
56
56
|
:to="{ ...$route, query: { ...$route.query, tab: '' } }"
|
|
57
|
-
:matched="!$route.query
|
|
57
|
+
:matched="!$route.query['tab']"
|
|
58
58
|
>
|
|
59
59
|
Controls
|
|
60
60
|
</BaseOverflowTab>
|
|
61
61
|
<BaseOverflowTab
|
|
62
62
|
:to="{ ...$route, query: { ...$route.query, tab: 'docs' } }"
|
|
63
|
-
:matched="$route.query
|
|
63
|
+
:matched="$route.query['tab'] === 'docs'"
|
|
64
64
|
:class="{
|
|
65
65
|
'opacity-70': !renderedDoc,
|
|
66
66
|
}"
|
|
@@ -69,7 +69,7 @@ const hasEvents = computed(() => eventsStore.events.length)
|
|
|
69
69
|
</BaseOverflowTab>
|
|
70
70
|
<BaseOverflowTab
|
|
71
71
|
:to="{ ...$route, query: { ...$route.query, tab: 'events' } }"
|
|
72
|
-
:matched="$route.query
|
|
72
|
+
:matched="$route.query['tab'] === 'events'"
|
|
73
73
|
:class="{
|
|
74
74
|
'opacity-70': !hasEvents,
|
|
75
75
|
}"
|
|
@@ -1,8 +1,9 @@
|
|
|
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
|
-
import { onClickOutside, useStorage, useTimeoutFn } from '@vueuse/core'
|
|
6
|
+
import { onClickOutside, useLiveAnnouncer, useStorage, useTimeoutFn } from '@vueuse/core'
|
|
6
7
|
import { computed, nextTick, onMounted, ref } from 'vue'
|
|
7
8
|
import { toPresetState, toRawDeep } from '../../util/state'
|
|
8
9
|
import BaseSelect from '../base/BaseSelect.vue'
|
|
@@ -41,7 +42,7 @@ const presetStates = useStorage<Map<string, { state: Record<string, unknown>, la
|
|
|
41
42
|
)
|
|
42
43
|
|
|
43
44
|
const presetsOptions = computed(() => {
|
|
44
|
-
const options = { [DEFAULT_ID]: 'Initial state' }
|
|
45
|
+
const options: Record<string, string> = { [DEFAULT_ID]: 'Initial state' }
|
|
45
46
|
presetStates.value.forEach((value, key) => {
|
|
46
47
|
options[key] = value.label
|
|
47
48
|
})
|
|
@@ -53,7 +54,7 @@ function resetState() {
|
|
|
53
54
|
applyState(props.variant.state, clone(defaultState))
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
function applyPreset(id) {
|
|
57
|
+
function applyPreset(id: string) {
|
|
57
58
|
if (id === DEFAULT_ID) {
|
|
58
59
|
resetState()
|
|
59
60
|
}
|
|
@@ -75,6 +76,7 @@ onMounted(() => {
|
|
|
75
76
|
|
|
76
77
|
const input = ref<HTMLInputElement>()
|
|
77
78
|
const select = ref<HTMLInputElement>()
|
|
79
|
+
const picker = ref<InstanceType<typeof BaseSelect>>()
|
|
78
80
|
const canEdit = computed(() => selectedOption.value !== DEFAULT_ID)
|
|
79
81
|
const isEditing = ref(false)
|
|
80
82
|
|
|
@@ -102,6 +104,7 @@ const editingLabel = computed({
|
|
|
102
104
|
})
|
|
103
105
|
|
|
104
106
|
const savedNotif = ref(false)
|
|
107
|
+
const { polite } = useLiveAnnouncer()
|
|
105
108
|
const savedTimeout = useTimeoutFn(() => {
|
|
106
109
|
savedNotif.value = false
|
|
107
110
|
}, 1000)
|
|
@@ -114,9 +117,10 @@ async function savePreset() {
|
|
|
114
117
|
preset.state = toPresetState(props.variant.state, omitKeys)
|
|
115
118
|
savedNotif.value = true
|
|
116
119
|
savedTimeout.start()
|
|
120
|
+
polite('Preset saved')
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
function deletePreset(id) {
|
|
123
|
+
function deletePreset(id: string) {
|
|
120
124
|
// @TODO custom confirm modal UI
|
|
121
125
|
// eslint-disable-next-line no-alert
|
|
122
126
|
if (!confirm('Are you sure you want to delete this preset?')) {
|
|
@@ -141,11 +145,20 @@ async function startEditing() {
|
|
|
141
145
|
input.value?.select()
|
|
142
146
|
}
|
|
143
147
|
|
|
144
|
-
function stopEditing() {
|
|
148
|
+
async function stopEditing(refocus = false) {
|
|
149
|
+
if (!isEditing.value) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
145
152
|
isEditing.value = false
|
|
153
|
+
// Finishing a rename from the keyboard unmounts the field that had focus, so
|
|
154
|
+
// hand it to the picker, which now shows the new name.
|
|
155
|
+
if (refocus) {
|
|
156
|
+
await nextTick()
|
|
157
|
+
picker.value?.focus()
|
|
158
|
+
}
|
|
146
159
|
}
|
|
147
160
|
|
|
148
|
-
onClickOutside(select, stopEditing)
|
|
161
|
+
onClickOutside(select, () => stopEditing())
|
|
149
162
|
</script>
|
|
150
163
|
|
|
151
164
|
<template>
|
|
@@ -154,79 +167,108 @@ onClickOutside(select, stopEditing)
|
|
|
154
167
|
ref="select"
|
|
155
168
|
class="flex-1 min-w-0"
|
|
156
169
|
>
|
|
170
|
+
<!-- Swapped for the picker rather than put inside it: a text field inside a
|
|
171
|
+
combobox is a control nested in a control (#828). -->
|
|
172
|
+
<input
|
|
173
|
+
v-if="isEditing"
|
|
174
|
+
ref="input"
|
|
175
|
+
v-model="editingLabel"
|
|
176
|
+
type="text"
|
|
177
|
+
aria-label="Preset name"
|
|
178
|
+
class="text-inherit bg-transparent w-full px-2 h-[27px] -my-1 border border-solid border-primary-500 rounded-sm outline-none"
|
|
179
|
+
@keydown.enter="stopEditing(true)"
|
|
180
|
+
@keydown.escape="stopEditing(true)"
|
|
181
|
+
>
|
|
157
182
|
<BaseSelect
|
|
183
|
+
v-else
|
|
184
|
+
ref="picker"
|
|
158
185
|
v-model="selectedOption"
|
|
186
|
+
label="Preset"
|
|
159
187
|
:options="presetsOptions"
|
|
160
188
|
@dblclick="startEditing()"
|
|
161
|
-
@keydown.enter="stopEditing()"
|
|
162
|
-
@keydown.escape="stopEditing()"
|
|
163
189
|
@select="id => applyPreset(id)"
|
|
164
|
-
|
|
165
|
-
<template #default="{ label }">
|
|
166
|
-
<input
|
|
167
|
-
v-if="isEditing"
|
|
168
|
-
ref="input"
|
|
169
|
-
v-model="editingLabel"
|
|
170
|
-
type="text"
|
|
171
|
-
class="text-inherit bg-transparent w-full h-full outline-none"
|
|
172
|
-
@click.stop.prevent
|
|
173
|
-
>
|
|
174
|
-
|
|
175
|
-
<div
|
|
176
|
-
v-else
|
|
177
|
-
class="flex items-center gap-2"
|
|
178
|
-
>
|
|
179
|
-
<span class="flex-1 truncate">
|
|
180
|
-
{{ label }}
|
|
181
|
-
</span>
|
|
182
|
-
<Icon
|
|
183
|
-
v-if="canEdit"
|
|
184
|
-
v-tooltip="'Rename this preset'"
|
|
185
|
-
icon="carbon:edit"
|
|
186
|
-
class="flex-none cursor-pointer w-4 h-4 hover:text-primary-500 opacity-50 hover:opacity-100 dark:hover:text-primary-400 text-gray-900 dark:text-gray-100"
|
|
187
|
-
@click.stop="startEditing()"
|
|
188
|
-
/>
|
|
189
|
-
</div>
|
|
190
|
-
</template>
|
|
191
|
-
|
|
192
|
-
<template #option="{ label, value }">
|
|
193
|
-
<div class="flex gap-2 items-center">
|
|
194
|
-
<span
|
|
195
|
-
class="flex-1 truncate"
|
|
196
|
-
>{{ label }}</span>
|
|
197
|
-
<Icon
|
|
198
|
-
v-if="value !== DEFAULT_ID"
|
|
199
|
-
v-tooltip="'Delete this preset'"
|
|
200
|
-
icon="carbon:trash-can"
|
|
201
|
-
class="flex-none cursor-pointer w-4 h-4 hover:text-primary-500 opacity-50 hover:opacity-100 dark:hover:text-primary-400 text-gray-900 dark:text-gray-100"
|
|
202
|
-
@click.stop="deletePreset(value)"
|
|
203
|
-
/>
|
|
204
|
-
</div>
|
|
205
|
-
</template>
|
|
206
|
-
</BaseSelect>
|
|
190
|
+
/>
|
|
207
191
|
</div>
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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>
|
|
231
273
|
</div>
|
|
232
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
|
-
<
|
|
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="{
|
|
33
|
-
<
|
|
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
|
-
|
|
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':
|
|
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
|
-
</
|
|
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
|
-
</
|
|
59
|
+
</BaseDropdown>
|
|
59
60
|
</template>
|
|
@@ -22,7 +22,7 @@ const innerOrientation = computed(() =>
|
|
|
22
22
|
const route = useRoute()
|
|
23
23
|
|
|
24
24
|
const panelContentComponent = computed(() => {
|
|
25
|
-
switch (route.query
|
|
25
|
+
switch (route.query['tab']) {
|
|
26
26
|
case 'docs':
|
|
27
27
|
return StoryDocs
|
|
28
28
|
case 'events':
|
|
@@ -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"
|