@poveste/app 0.13.1 → 0.15.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/bundled/App.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/app.css +31 -31
- package/dist/bundled/components/HomeView.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/app/AppLogo.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/bundled/components/app/Breadcrumb.js +1 -1
- package/dist/bundled/components/app/Breadcrumb.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/bundled/components/app/TopBar.vue_vue_type_script_setup_true_lang.js +4 -3
- package/dist/bundled/components/app/TopBarChipItem.js +1 -1
- package/dist/bundled/components/base/BaseCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/base/BaseEmpty.js +2 -2
- package/dist/bundled/components/base/BaseOverflowMenu.vue_vue_type_script_setup_true_lang.js +18 -15
- package/dist/bundled/components/base/BaseSelect.vue_vue_type_script_setup_true_lang.js +158 -26
- package/dist/bundled/components/base/BaseSplitPane.js +1 -1
- package/dist/bundled/components/base/BaseSplitPane.vue_vue_type_script_setup_true_lang.js +6 -4
- package/dist/bundled/components/command/CommandPrompts.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/command/CommandPromptsModal.vue_vue_type_script_setup_true_lang.js +4 -3
- package/dist/bundled/components/command/PromptSelect.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/bundled/components/command/PromptText.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/panel/ControlsComponentPropItem.vue_vue_type_script_setup_true_lang.js +20 -14
- 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 +94 -55
- package/dist/bundled/components/panel/StoryControls.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/panel/StoryDocs.vue_vue_type_script_setup_true_lang.js +3 -1
- package/dist/bundled/components/panel/StoryEvent.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/bundled/components/panel/StoryEvents.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/bundled/components/panel/StorySidePanel.vue_vue_type_script_setup_true_lang.js +8 -6
- package/dist/bundled/components/panel/StorySourceCode.js +1 -1
- package/dist/bundled/components/panel/StorySourceCode.vue_vue_type_script_setup_true_lang.js +8 -6
- 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 +16 -9
- package/dist/bundled/components/story/GenericMountStory.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/bundled/components/story/GenericRenderStory.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/bundled/components/story/StoryResponsivePreview.js +1 -1
- package/dist/bundled/components/story/StoryResponsivePreview.vue_vue_type_script_setup_true_lang.js +23 -8
- package/dist/bundled/components/story/StoryVariantGrid.js +1 -1
- package/dist/bundled/components/story/StoryVariantGrid.vue_vue_type_script_setup_true_lang.js +15 -10
- package/dist/bundled/components/story/StoryVariantGridItem.js +1 -1
- package/dist/bundled/components/story/StoryVariantGridItem.vue_vue_type_script_setup_true_lang.js +3 -3
- 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 +7 -3
- package/dist/bundled/components/story/StoryViewer.js +1 -1
- package/dist/bundled/components/story/StoryViewer.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/bundled/components/toolbar/ToolbarBackground.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/bundled/components/toolbar/ToolbarResponsiveSize.vue_vue_type_script_setup_true_lang.js +17 -16
- 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/plugin.js +4 -3
- package/dist/bundled/sandbox.js +5 -4
- package/dist/bundled/stores/folder.js +1 -1
- package/dist/bundled/stores/story.js +2 -2
- package/dist/bundled/util/commands.js +21 -10
- package/dist/bundled/util/dark.js +1 -1
- package/dist/bundled/util/docs.js +2 -1
- package/dist/bundled/util/events.js +1 -1
- package/dist/bundled/util/grid-slots.js +2 -2
- package/dist/bundled/util/icons.generated.js +0 -1
- package/dist/bundled/util/keyboard.js +9 -14
- package/dist/bundled/util/mapping.js +3 -3
- package/dist/bundled/util/preview-settings.js +1 -1
- package/dist/bundled/util/render-tracker.js +33 -0
- package/dist/bundled/util/variant.js +12 -2
- package/dist/style.css +23 -1
- package/package.json +14 -18
- package/src/__tests__/render-tracker.spec.ts +38 -0
- package/src/app/App.vue +7 -4
- package/src/app/components/HomeView.vue +1 -1
- package/src/app/components/app/AppLogo.vue +6 -2
- package/src/app/components/app/Breadcrumb.vue +2 -1
- package/src/app/components/app/TopBar.vue +7 -2
- package/src/app/components/app/TopBarChipItem.vue +1 -1
- package/src/app/components/base/BaseCheckbox.vue +1 -1
- 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 +25 -18
- package/src/app/components/base/BaseSelect.vue +171 -15
- package/src/app/components/base/BaseSplitPane.vue +11 -6
- package/src/app/components/command/CommandPrompts.vue +1 -1
- package/src/app/components/command/CommandPromptsModal.vue +1 -0
- package/src/app/components/command/PromptSelect.vue +3 -2
- package/src/app/components/command/PromptText.vue +7 -3
- package/src/app/components/panel/ControlsComponentPropItem.vue +26 -15
- package/src/app/components/panel/PaneTabs.vue +6 -6
- package/src/app/components/panel/StatePresets.vue +125 -68
- package/src/app/components/panel/StoryControls.vue +1 -1
- package/src/app/components/panel/StoryDocs.vue +3 -1
- package/src/app/components/panel/StoryEvent.vue +7 -2
- package/src/app/components/panel/StoryEvents.vue +4 -1
- package/src/app/components/panel/StorySidePanel.vue +3 -1
- package/src/app/components/panel/StorySourceCode.vue +12 -8
- package/src/app/components/search/SearchItemContent.vue +2 -1
- package/src/app/components/search/SearchPane.vue +16 -8
- package/src/app/components/story/GenericMountStory.vue +2 -1
- package/src/app/components/story/GenericRenderStory.vue +2 -1
- package/src/app/components/story/StoryResponsivePreview.vue +36 -7
- package/src/app/components/story/StoryVariantGrid.vue +33 -16
- package/src/app/components/story/StoryVariantGridItem.vue +1 -1
- package/src/app/components/story/StoryVariantSinglePreviewRemote.vue +14 -8
- package/src/app/components/story/StoryView.vue +10 -4
- package/src/app/components/story/StoryViewer.vue +3 -3
- package/src/app/components/toolbar/ToolbarBackground.vue +1 -1
- package/src/app/components/toolbar/ToolbarResponsiveSize.vue +23 -3
- 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/plugin.ts +7 -3
- package/src/app/sandbox.ts +9 -3
- package/src/app/stores/folder.ts +1 -1
- package/src/app/stores/story.ts +2 -2
- package/src/app/types.ts +6 -6
- package/src/app/util/commands.ts +27 -17
- package/src/app/util/dark.ts +1 -1
- package/src/app/util/docs.ts +2 -1
- package/src/app/util/env.ts +0 -1
- package/src/app/util/events.ts +6 -5
- package/src/app/util/grid-slots.ts +2 -2
- package/src/app/util/icons.generated.ts +1 -1
- package/src/app/util/keyboard.ts +10 -11
- package/src/app/util/mapping.ts +3 -3
- package/src/app/util/preview-settings.ts +1 -1
- package/src/app/util/render-tracker.ts +31 -0
- package/src/app/util/scroll.ts +5 -1
- package/src/app/util/state.ts +4 -4
- package/src/app/util/variant.ts +16 -3
- package/src/app/components/base/BaseCopyIcon.vue +0 -34
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
+
import type { SetupContext, VNode } from 'vue'
|
|
2
3
|
import { Icon } from '@iconify/vue'
|
|
3
4
|
import { useResizeObserver } from '@vueuse/core'
|
|
4
5
|
import { computed, h, onBeforeUnmount, reactive, ref } from 'vue'
|
|
@@ -11,9 +12,9 @@ const el = ref<HTMLDivElement>()
|
|
|
11
12
|
|
|
12
13
|
const availableWidth = ref(0)
|
|
13
14
|
|
|
14
|
-
useResizeObserver(el, (
|
|
15
|
-
|
|
16
|
-
availableWidth.value =
|
|
15
|
+
useResizeObserver(el, ([entry]) => {
|
|
16
|
+
if (!entry) return
|
|
17
|
+
availableWidth.value = entry.contentRect.width - overflowButtonWidth
|
|
17
18
|
})
|
|
18
19
|
|
|
19
20
|
// Children
|
|
@@ -28,8 +29,8 @@ const children = ref(new Map<HTMLElement, ChildState>())
|
|
|
28
29
|
const visibleChildrenCount = computed(() => {
|
|
29
30
|
let width = 0
|
|
30
31
|
const c = [...children.value.values()].sort((a, b) => a.index - b.index)
|
|
31
|
-
for (
|
|
32
|
-
width +=
|
|
32
|
+
for (const [i, child] of c.entries()) {
|
|
33
|
+
width += child.width
|
|
33
34
|
if (width > availableWidth.value) {
|
|
34
35
|
return i
|
|
35
36
|
}
|
|
@@ -43,43 +44,49 @@ const visibleChildrenCount = computed(() => {
|
|
|
43
44
|
const ChildWrapper = {
|
|
44
45
|
name: 'ChildWrapper',
|
|
45
46
|
props: ['index'],
|
|
46
|
-
setup(props, { slots }) {
|
|
47
|
+
setup(props: { index: number }, { slots }: SetupContext) {
|
|
47
48
|
const el = ref<HTMLDivElement>()
|
|
48
49
|
|
|
49
50
|
const state = reactive({ width: 0, index: props.index })
|
|
50
51
|
|
|
51
|
-
useResizeObserver(el, (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
useResizeObserver(el, ([entry]) => {
|
|
53
|
+
// The observer only fires for an element it is observing, so this is
|
|
54
|
+
// never null here — but `children` is keyed by the element, and an
|
|
55
|
+
// undefined key would silently make a second entry for the same child.
|
|
56
|
+
const element = el.value
|
|
57
|
+
if (!element || !entry) return
|
|
58
|
+
const width = entry.contentRect.width
|
|
59
|
+
if (!children.value.has(element)) {
|
|
60
|
+
children.value.set(element, state)
|
|
55
61
|
}
|
|
56
62
|
state.width = width
|
|
57
63
|
})
|
|
58
64
|
|
|
59
65
|
onBeforeUnmount(() => {
|
|
60
|
-
|
|
66
|
+
const element = el.value
|
|
67
|
+
if (element) children.value.delete(element)
|
|
61
68
|
})
|
|
62
69
|
|
|
63
70
|
const visible = computed(() => visibleChildrenCount.value > state.index)
|
|
64
71
|
|
|
65
|
-
return () => h('div', { ref: el, style: { visibility: visible.value ? 'visible' : 'hidden' } }, slots
|
|
72
|
+
return () => h('div', { ref: el, style: { visibility: visible.value ? 'visible' : 'hidden' } }, slots['default']?.())
|
|
66
73
|
},
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
/**
|
|
70
77
|
* Wraps each child with a <ChildWrapper>
|
|
71
78
|
*/
|
|
72
|
-
function ChildrenRender(
|
|
73
|
-
const
|
|
74
|
-
return
|
|
79
|
+
function ChildrenRender(_props: unknown, { slots }: SetupContext) {
|
|
80
|
+
const children = slots['default']?.()[0]?.children
|
|
81
|
+
return Array.isArray(children) ? children.map((vnode, index) => h(ChildWrapper, { index }, () => [vnode])) : []
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
/**
|
|
78
85
|
* Only renders a part of a children list
|
|
79
86
|
*/
|
|
80
|
-
function ChildrenSlice(props, { slots }) {
|
|
81
|
-
const
|
|
82
|
-
return
|
|
87
|
+
function ChildrenSlice(props: { start?: number, end?: number }, { slots }: SetupContext) {
|
|
88
|
+
const children = slots['default']?.()[0]?.children
|
|
89
|
+
return Array.isArray(children) ? children.slice(props.start, props.end) as VNode[] : []
|
|
83
90
|
}
|
|
84
91
|
</script>
|
|
85
92
|
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { ComputedRef } from 'vue'
|
|
3
3
|
import { Icon } from '@iconify/vue'
|
|
4
|
+
import { refAutoReset } from '@vueuse/core'
|
|
4
5
|
import { Dropdown as VDropdown } from 'floating-vue'
|
|
5
|
-
import { computed } from 'vue'
|
|
6
|
+
import { computed, ref, useId } from 'vue'
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* The WAI-ARIA select-only combobox: the trigger keeps focus and names the active
|
|
10
|
+
* option through `aria-activedescendant`, so the popup needs no focus of its own.
|
|
11
|
+
* It was a click-only `<div>` with `<div>` options, which a keyboard could not open
|
|
12
|
+
* and a screen reader did not list (#828).
|
|
13
|
+
*/
|
|
6
14
|
|
|
7
15
|
const props = defineProps<{
|
|
8
16
|
modelValue: string
|
|
9
17
|
options: Record<string, string> | Array<string>
|
|
18
|
+
/** The accessible name of the picker and its list. */
|
|
19
|
+
label: string
|
|
10
20
|
}>()
|
|
11
21
|
|
|
12
22
|
const emit = defineEmits<{
|
|
@@ -21,23 +31,159 @@ const formattedOptions: ComputedRef<Record<string, string>> = computed(() => {
|
|
|
21
31
|
return props.options
|
|
22
32
|
})
|
|
23
33
|
|
|
34
|
+
const entries = computed(() => Object.entries(formattedOptions.value))
|
|
35
|
+
|
|
24
36
|
const selectedLabel = computed(() => formattedOptions.value[props.modelValue])
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
const id = useId()
|
|
39
|
+
const listboxId = `${id}-listbox`
|
|
40
|
+
function optionId(index: number) {
|
|
41
|
+
return `${id}-option-${index}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const combobox = ref<HTMLElement>()
|
|
45
|
+
const open = ref(false)
|
|
46
|
+
const activeIndex = ref(-1)
|
|
47
|
+
|
|
48
|
+
function openList(index?: number) {
|
|
49
|
+
const selected = entries.value.findIndex(([value]) => value === props.modelValue)
|
|
50
|
+
activeIndex.value = index ?? Math.max(selected, 0)
|
|
51
|
+
open.value = true
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* Type-to-select, as the pattern recommends: typed characters within half a second
|
|
56
|
+
* form one search, and repeating a single character steps through the options
|
|
57
|
+
* that start with it.
|
|
58
|
+
*/
|
|
59
|
+
const typed = refAutoReset('', 500)
|
|
60
|
+
|
|
61
|
+
function isTypeaheadKey(event: KeyboardEvent) {
|
|
62
|
+
if (event.ctrlKey || event.metaKey || event.altKey || event.key.length !== 1) {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
// Space chooses, unless it continues a search already under way.
|
|
66
|
+
return event.key !== ' ' || typed.value !== ''
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function typeahead(char: string) {
|
|
70
|
+
const previous = typed.value
|
|
71
|
+
typed.value = previous + char.toLowerCase()
|
|
72
|
+
const repeated = [...typed.value].every(c => c === typed.value[0])
|
|
73
|
+
const search = repeated ? typed.value.charAt(0) : typed.value
|
|
74
|
+
// A new or repeated character looks past the active option; a longer search may
|
|
75
|
+
// still match it.
|
|
76
|
+
const start = repeated ? activeIndex.value + 1 : Math.max(activeIndex.value, 0)
|
|
77
|
+
const count = entries.value.length
|
|
78
|
+
for (let offset = 0; offset < count; offset++) {
|
|
79
|
+
const index = (start + offset) % count
|
|
80
|
+
if (entries.value[index]?.[1].toLowerCase().startsWith(search)) {
|
|
81
|
+
activeIndex.value = index
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function close() {
|
|
88
|
+
open.value = false
|
|
30
89
|
}
|
|
90
|
+
|
|
91
|
+
function choose(index: number) {
|
|
92
|
+
const entry = entries.value[index]
|
|
93
|
+
if (entry) {
|
|
94
|
+
emit('update:modelValue', entry[0])
|
|
95
|
+
emit('select', entry[0])
|
|
96
|
+
}
|
|
97
|
+
close()
|
|
98
|
+
combobox.value?.focus()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function onKeydown(event: KeyboardEvent) {
|
|
102
|
+
const last = entries.value.length - 1
|
|
103
|
+
|
|
104
|
+
if (isTypeaheadKey(event)) {
|
|
105
|
+
event.preventDefault()
|
|
106
|
+
if (!open.value) {
|
|
107
|
+
openList()
|
|
108
|
+
}
|
|
109
|
+
typeahead(event.key)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!open.value) {
|
|
114
|
+
if (['ArrowDown', 'ArrowUp', 'Enter', ' ', 'Home', 'End'].includes(event.key)) {
|
|
115
|
+
event.preventDefault()
|
|
116
|
+
openList(event.key === 'Home' ? 0 : event.key === 'End' ? last : undefined)
|
|
117
|
+
}
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
switch (event.key) {
|
|
122
|
+
case 'ArrowDown':
|
|
123
|
+
event.preventDefault()
|
|
124
|
+
activeIndex.value = Math.min(activeIndex.value + 1, last)
|
|
125
|
+
break
|
|
126
|
+
case 'ArrowUp':
|
|
127
|
+
event.preventDefault()
|
|
128
|
+
if (event.altKey) {
|
|
129
|
+
choose(activeIndex.value)
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
activeIndex.value = Math.max(activeIndex.value - 1, 0)
|
|
133
|
+
}
|
|
134
|
+
break
|
|
135
|
+
case 'Home':
|
|
136
|
+
event.preventDefault()
|
|
137
|
+
activeIndex.value = 0
|
|
138
|
+
break
|
|
139
|
+
case 'End':
|
|
140
|
+
event.preventDefault()
|
|
141
|
+
activeIndex.value = last
|
|
142
|
+
break
|
|
143
|
+
case 'Enter':
|
|
144
|
+
case ' ':
|
|
145
|
+
event.preventDefault()
|
|
146
|
+
choose(activeIndex.value)
|
|
147
|
+
break
|
|
148
|
+
case 'Escape':
|
|
149
|
+
event.preventDefault()
|
|
150
|
+
close()
|
|
151
|
+
break
|
|
152
|
+
case 'Tab':
|
|
153
|
+
// Keeps the default: focus moves on, with the active option chosen.
|
|
154
|
+
choose(activeIndex.value)
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
defineExpose({
|
|
160
|
+
focus: () => combobox.value?.focus(),
|
|
161
|
+
})
|
|
31
162
|
</script>
|
|
32
163
|
|
|
33
164
|
<template>
|
|
165
|
+
<!-- `no-auto-focus`: floating-vue otherwise moves focus into the popup once it
|
|
166
|
+
shows, and the combobox pattern keeps it on the trigger, which owns the keys. -->
|
|
34
167
|
<VDropdown
|
|
168
|
+
v-model:shown="open"
|
|
35
169
|
class="poveste-base-select"
|
|
170
|
+
:triggers="[]"
|
|
171
|
+
no-auto-focus
|
|
36
172
|
auto-size
|
|
37
173
|
auto-boundary-max-size
|
|
38
174
|
>
|
|
39
175
|
<div
|
|
40
|
-
|
|
176
|
+
ref="combobox"
|
|
177
|
+
role="combobox"
|
|
178
|
+
tabindex="0"
|
|
179
|
+
:aria-label="label"
|
|
180
|
+
aria-haspopup="listbox"
|
|
181
|
+
:aria-expanded="open"
|
|
182
|
+
:aria-controls="open ? listboxId : undefined"
|
|
183
|
+
:aria-activedescendant="open && activeIndex >= 0 ? optionId(activeIndex) : undefined"
|
|
184
|
+
class="cursor-pointer w-full outline-none focus-visible:border-primary-500 px-2 h-[27px] -my-1 border border-solid border-black/25 dark:border-white/25 hover:border-primary-500 dark:hover:border-primary-500 rounded-sm flex gap-2 items-center leading-normal"
|
|
185
|
+
@click="open ? close() : openList()"
|
|
186
|
+
@keydown="onKeydown"
|
|
41
187
|
>
|
|
42
188
|
<div class="flex-1 truncate">
|
|
43
189
|
<slot :label="selectedLabel">
|
|
@@ -49,24 +195,34 @@ function selectValue(value: string, hide: () => void) {
|
|
|
49
195
|
class="w-4 h-4 flex-none ml-auto"
|
|
50
196
|
/>
|
|
51
197
|
</div>
|
|
52
|
-
<template #popper
|
|
53
|
-
<div
|
|
198
|
+
<template #popper>
|
|
199
|
+
<div
|
|
200
|
+
:id="listboxId"
|
|
201
|
+
role="listbox"
|
|
202
|
+
:aria-label="label"
|
|
203
|
+
class="flex flex-col bg-gray-50 dark:bg-gray-700"
|
|
204
|
+
>
|
|
54
205
|
<div
|
|
55
|
-
v-for="(
|
|
56
|
-
|
|
57
|
-
:key="
|
|
206
|
+
v-for="([value, optionLabel], index) of entries"
|
|
207
|
+
:id="optionId(index)"
|
|
208
|
+
:key="value"
|
|
209
|
+
role="option"
|
|
210
|
+
:aria-selected="value === modelValue"
|
|
58
211
|
class="px-2 py-1 cursor-pointer hover:bg-primary-100 dark:hover:bg-primary-700"
|
|
59
212
|
:class="{
|
|
60
|
-
'bg-primary-200 dark:bg-primary-800':
|
|
213
|
+
'bg-primary-200 dark:bg-primary-800': value === modelValue,
|
|
214
|
+
'outline outline-2 -outline-offset-2 outline-primary-500': index === activeIndex,
|
|
61
215
|
}"
|
|
62
|
-
@
|
|
216
|
+
@mousedown.prevent
|
|
217
|
+
@mousemove="activeIndex = index"
|
|
218
|
+
@click="choose(index)"
|
|
63
219
|
>
|
|
64
220
|
<slot
|
|
65
221
|
name="option"
|
|
66
|
-
:label="
|
|
222
|
+
:label="optionLabel"
|
|
67
223
|
:value="value"
|
|
68
224
|
>
|
|
69
|
-
{{
|
|
225
|
+
{{ optionLabel }}
|
|
70
226
|
</slot>
|
|
71
227
|
</div>
|
|
72
228
|
</div>
|
|
@@ -168,15 +168,15 @@ const rightStyle = computed(() => {
|
|
|
168
168
|
if (!hasBothPanes.value) {
|
|
169
169
|
return { [axis.value]: '100%' }
|
|
170
170
|
}
|
|
171
|
-
return { [axis.value]: props.fixed ?
|
|
171
|
+
return { [axis.value]: props.fixed ? undefined : `${100 - boundSplit.value}%` }
|
|
172
172
|
})
|
|
173
173
|
|
|
174
174
|
const dragging = ref(false)
|
|
175
175
|
let startPosition = 0
|
|
176
176
|
let startSplit = 0
|
|
177
|
-
const el = ref(
|
|
177
|
+
const el = ref<HTMLElement>()
|
|
178
178
|
|
|
179
|
-
function dragStart(e) {
|
|
179
|
+
function dragStart(e: MouseEvent) {
|
|
180
180
|
dragging.value = true
|
|
181
181
|
startPosition = props.orientation === 'landscape' ? e.pageX : e.pageY
|
|
182
182
|
startSplit = boundSplit.value
|
|
@@ -184,17 +184,22 @@ function dragStart(e) {
|
|
|
184
184
|
window.addEventListener('mouseup', dragEnd)
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
function dragMove(e) {
|
|
187
|
+
function dragMove(e: MouseEvent) {
|
|
188
188
|
if (dragging.value) {
|
|
189
|
+
// The move listener is on `window`, so it can outlive the element for the
|
|
190
|
+
// rest of a drag if the pane unmounts under it.
|
|
191
|
+
const pane = el.value
|
|
192
|
+
if (!pane) return
|
|
193
|
+
|
|
189
194
|
let position
|
|
190
195
|
let totalSize
|
|
191
196
|
if (props.orientation === 'landscape') {
|
|
192
197
|
position = e.pageX
|
|
193
|
-
totalSize =
|
|
198
|
+
totalSize = pane.offsetWidth
|
|
194
199
|
}
|
|
195
200
|
else {
|
|
196
201
|
position = e.pageY
|
|
197
|
-
totalSize =
|
|
202
|
+
totalSize = pane.offsetHeight
|
|
198
203
|
}
|
|
199
204
|
const dPosition = position - startPosition
|
|
200
205
|
if (props.fixed) {
|
|
@@ -24,7 +24,7 @@ const promptTypes = {
|
|
|
24
24
|
const answers = reactive<Record<string, any>>({})
|
|
25
25
|
|
|
26
26
|
// Initial default values
|
|
27
|
-
for (const prompt of props.command.prompts) {
|
|
27
|
+
for (const prompt of props.command.prompts ?? []) {
|
|
28
28
|
let defaultValue
|
|
29
29
|
if (typeof prompt.defaultValue === 'function') {
|
|
30
30
|
defaultValue = prompt.defaultValue(answers)
|
|
@@ -33,6 +33,7 @@ const commandStore = useCommandStore()
|
|
|
33
33
|
/>
|
|
34
34
|
<div class="bg-white dark:bg-gray-900 md:mt-16 md:mx-auto w-screen max-w-[512px] max-h-[80vh] overflow-y-auto scroll-smooth shadow-xl border border-gray-200 dark:border-gray-750 rounded-lg relative divide-y divide-gray-200 dark:divide-gray-850">
|
|
35
35
|
<CommandPrompts
|
|
36
|
+
v-if="commandStore.selectedCommand"
|
|
36
37
|
:command="commandStore.selectedCommand"
|
|
37
38
|
@close="close()"
|
|
38
39
|
/>
|
|
@@ -16,8 +16,9 @@ const emit = defineEmits<{
|
|
|
16
16
|
(e: 'next'): void
|
|
17
17
|
}>()
|
|
18
18
|
|
|
19
|
+
// Optional coming in, required going out, so the empty case settles here.
|
|
19
20
|
const model = computed({
|
|
20
|
-
get: () => props.modelValue,
|
|
21
|
+
get: () => props.modelValue ?? '',
|
|
21
22
|
set: (value) => {
|
|
22
23
|
emit('update:modelValue', value)
|
|
23
24
|
emit('next')
|
|
@@ -76,7 +77,7 @@ const {
|
|
|
76
77
|
} = useSelection(formattedOptions)
|
|
77
78
|
|
|
78
79
|
function selectIndex(index: number) {
|
|
79
|
-
const result = formattedOptions.value[index]
|
|
80
|
+
const result = formattedOptions.value[index]?.value
|
|
80
81
|
if (result) {
|
|
81
82
|
model.value = result
|
|
82
83
|
}
|
|
@@ -9,11 +9,15 @@ const props = defineProps<{
|
|
|
9
9
|
}>()
|
|
10
10
|
|
|
11
11
|
const emit = defineEmits<{
|
|
12
|
-
(e: 'update:modelValue', value: string): void
|
|
12
|
+
(e: 'update:modelValue', value: string | undefined): void
|
|
13
13
|
}>()
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// The input needs a string to bind to, so the getter fills in `''` — but an
|
|
16
|
+
// absent answer has to leave as absent. `''` is falsy and *not* nullish, so
|
|
17
|
+
// coercing on the way out would stop a command's `answers.x ?? fallback` from
|
|
18
|
+
// firing, which is the trap #440 was about one file over.
|
|
19
|
+
const model = computed<string | undefined>({
|
|
20
|
+
get: () => props.modelValue ?? '',
|
|
17
21
|
set: value => emit('update:modelValue', value),
|
|
18
22
|
})
|
|
19
23
|
|
|
@@ -50,24 +50,35 @@ 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
|
-
<
|
|
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
|
+
<button
|
|
68
|
+
v-tooltip="'Remove override'"
|
|
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"
|
|
73
|
+
:class="[
|
|
74
|
+
canReset ? 'opacity-50 hover:opacity-100 focus-visible:opacity-100' : 'opacity-25 pointer-events-none',
|
|
75
|
+
]"
|
|
76
|
+
@click="reset()"
|
|
77
|
+
>
|
|
62
78
|
<Icon
|
|
63
|
-
v-tooltip="'Remove override'"
|
|
64
79
|
icon="carbon:erase"
|
|
65
|
-
class="
|
|
66
|
-
:class="[
|
|
67
|
-
canReset ? 'opacity-50 hover:opacity-100' : 'opacity-25 pointer-events-none',
|
|
68
|
-
]"
|
|
69
|
-
@click.stop="reset()"
|
|
80
|
+
class="w-4 h-4"
|
|
70
81
|
/>
|
|
71
|
-
</
|
|
72
|
-
</
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
73
84
|
</template>
|
|
@@ -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
|
}"
|