@poveste/plugin-vue 0.1.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/LICENSE +22 -0
- package/client.d.ts +2 -0
- package/collect.d.ts +2 -0
- package/components.d.ts +239 -0
- package/dist/bundled/client/app/MountStory.js +55 -0
- package/dist/bundled/client/app/RenderStory.js +74 -0
- package/dist/bundled/client/app/RouterLinkStub.js +40 -0
- package/dist/bundled/client/app/Story.js +112 -0
- package/dist/bundled/client/app/Variant.js +104 -0
- package/dist/bundled/client/app/auto-props.js +66 -0
- package/dist/bundled/client/app/global-components.js +81 -0
- package/dist/bundled/client/app/host.js +84 -0
- package/dist/bundled/client/app/index.js +6 -0
- package/dist/bundled/client/app/render-context.js +12 -0
- package/dist/bundled/client/app/util.js +70 -0
- package/dist/bundled/client/client.js +10 -0
- package/dist/bundled/client/codegen.js +196 -0
- package/dist/bundled/client/server/Story.js +79 -0
- package/dist/bundled/client/server/Variant.js +46 -0
- package/dist/bundled/client/server/run.js +51 -0
- package/dist/bundled/client/server/stub.js +8 -0
- package/dist/bundled/client/server.js +4 -0
- package/dist/bundled/node_modules/.pnpm/change-case@5.4.4/node_modules/change-case/dist/index.js +118 -0
- package/dist/client/app/MountStory.d.ts +16 -0
- package/dist/client/app/RenderStory.d.ts +38 -0
- package/dist/client/app/RouterLinkStub.d.ts +21 -0
- package/dist/client/app/Story.d.ts +33 -0
- package/dist/client/app/Variant.d.ts +78 -0
- package/dist/client/app/auto-props.d.ts +2 -0
- package/dist/client/app/global-components.d.ts +2 -0
- package/dist/client/app/host.d.ts +16 -0
- package/dist/client/app/index.d.ts +2 -0
- package/dist/client/app/render-context.d.ts +12 -0
- package/dist/client/app/util.d.ts +16 -0
- package/dist/client/client.d.ts +7 -0
- package/dist/client/codegen.d.ts +4 -0
- package/dist/client/server/Story.d.ts +81 -0
- package/dist/client/server/Variant.d.ts +52 -0
- package/dist/client/server/run.d.ts +2 -0
- package/dist/client/server/stub.d.ts +1 -0
- package/dist/client/server.d.ts +1 -0
- package/dist/commands/generate-story.client.d.ts +3 -0
- package/dist/commands/generate-story.client.js +27 -0
- package/dist/commands/generate-story.server.d.ts +3 -0
- package/dist/commands/generate-story.server.js +45 -0
- package/dist/helpers.d.ts +10 -0
- package/dist/helpers.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.node.d.ts +3 -0
- package/dist/index.node.js +54 -0
- package/dist/util/list-components.d.ts +1 -0
- package/dist/util/list-components.js +15 -0
- package/package.json +71 -0
- package/src/client/app/MountStory.ts +83 -0
- package/src/client/app/RenderStory.ts +131 -0
- package/src/client/app/RouterLinkStub.ts +48 -0
- package/src/client/app/Story.ts +209 -0
- package/src/client/app/Variant.ts +196 -0
- package/src/client/app/auto-props.ts +135 -0
- package/src/client/app/global-components.ts +131 -0
- package/src/client/app/host.ts +139 -0
- package/src/client/app/index.ts +2 -0
- package/src/client/app/render-context.ts +23 -0
- package/src/client/app/util.ts +129 -0
- package/src/client/client.ts +8 -0
- package/src/client/codegen.ts +400 -0
- package/src/client/server/Story.ts +121 -0
- package/src/client/server/Variant.ts +56 -0
- package/src/client/server/run.ts +63 -0
- package/src/client/server/stub.ts +12 -0
- package/src/client/server.ts +1 -0
- package/src/commands/generate-story.client.ts +29 -0
- package/src/commands/generate-story.server.ts +55 -0
- package/src/helpers.ts +15 -0
- package/src/index.node.ts +63 -0
- package/src/index.ts +1 -0
- package/src/util/list-components.ts +16 -0
- package/tsconfig.build.json +6 -0
- package/tsconfig.json +40 -0
- package/vite.config.ts +70 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { Variant } from '@poveste/shared'
|
|
2
|
+
import type { ComputedRef, PropType } from 'vue'
|
|
3
|
+
import { applyState } from '@poveste/shared'
|
|
4
|
+
import { computed, defineComponent, getCurrentInstance, inject, onBeforeUnmount, useAttrs } from 'vue'
|
|
5
|
+
import { syncVariantAutoProps } from './auto-props.js'
|
|
6
|
+
import { useRenderContext } from './render-context.js'
|
|
7
|
+
import { syncStateBundledAndExternal, toRawDeep } from './util.js'
|
|
8
|
+
|
|
9
|
+
// const logLocation = location.href.includes('__sandbox') ? '[Sandbox]' : '[Host]'
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
|
|
13
|
+
name: 'Variant',
|
|
14
|
+
__histoireType: 'variant',
|
|
15
|
+
|
|
16
|
+
props: {
|
|
17
|
+
initState: {
|
|
18
|
+
type: Function as PropType<() => any | Promise<any>>,
|
|
19
|
+
default: undefined,
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
source: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: undefined,
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
responsiveDisabled: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
default: false,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
autoPropsDisabled: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
default: false,
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
setupApp: {
|
|
38
|
+
type: Function,
|
|
39
|
+
default: undefined,
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
meta: {
|
|
43
|
+
type: Object as PropType<Variant['meta']>,
|
|
44
|
+
default: undefined,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
implicit: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: false,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async setup(props) {
|
|
54
|
+
const attrs = useAttrs() as {
|
|
55
|
+
variant?: Variant
|
|
56
|
+
}
|
|
57
|
+
const vm = getCurrentInstance()
|
|
58
|
+
const story = inject<ComputedRef<any>>('story')
|
|
59
|
+
const implicitState = inject<() => any>('implicitState')
|
|
60
|
+
const renderContext = useRenderContext()
|
|
61
|
+
let lastPropsTypesSnapshot: string
|
|
62
|
+
let renderVariant: Variant
|
|
63
|
+
let renderStateSync: ReturnType<typeof syncStateBundledAndExternal>
|
|
64
|
+
|
|
65
|
+
const mountVariant = computed(() => attrs.variant)
|
|
66
|
+
|
|
67
|
+
if (renderContext?.mode !== 'render' && typeof props.initState === 'function' && mountVariant.value) {
|
|
68
|
+
const state = await props.initState()
|
|
69
|
+
applyState(mountVariant.value.state, toRawDeep(state))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (renderContext?.mode !== 'render' && mountVariant.value && implicitState) {
|
|
73
|
+
syncStateBundledAndExternal(mountVariant.value.state, implicitState())
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function updateVariant(variant: Variant) {
|
|
77
|
+
Object.assign(variant, {
|
|
78
|
+
slots: () => vm.proxy.$slots,
|
|
79
|
+
source: props.source,
|
|
80
|
+
responsiveDisabled: props.responsiveDisabled,
|
|
81
|
+
autoPropsDisabled: props.autoPropsDisabled,
|
|
82
|
+
setupApp: props.setupApp,
|
|
83
|
+
meta: props.meta,
|
|
84
|
+
configReady: true,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
if (!props.implicit && !story.value?.meta?.hasVariantChildComponents) {
|
|
88
|
+
if (!story.value.meta) {
|
|
89
|
+
story.value.meta = {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Object.assign(story.value.meta, {
|
|
93
|
+
hasVariantChildComponents: true,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function resolveVariant() {
|
|
99
|
+
if (attrs.variant) {
|
|
100
|
+
return attrs.variant
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (renderContext?.mode !== 'render') {
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!renderVariant) {
|
|
108
|
+
renderVariant = story.value?.variants[renderContext.nextVariantIndex.value]
|
|
109
|
+
renderContext.nextVariantIndex.value++
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return renderVariant
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function renderVariantSlot(variant: Variant) {
|
|
116
|
+
if (renderContext.slotName === 'controls') {
|
|
117
|
+
return vm.proxy.$slots.controls?.({
|
|
118
|
+
state: renderContext.externalState,
|
|
119
|
+
}) ?? null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (renderContext.slotName !== 'default') {
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const vnodes = vm.proxy.$slots.default?.({
|
|
127
|
+
state: renderContext.externalState,
|
|
128
|
+
}) ?? null
|
|
129
|
+
|
|
130
|
+
if (vnodes && !variant.autoPropsDisabled) {
|
|
131
|
+
lastPropsTypesSnapshot = syncVariantAutoProps(
|
|
132
|
+
variant,
|
|
133
|
+
vnodes,
|
|
134
|
+
renderContext.externalState,
|
|
135
|
+
lastPropsTypesSnapshot,
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return vnodes
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function syncRenderVariantState(variant: Variant) {
|
|
143
|
+
if (!implicitState) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const shouldSync = renderContext?.mode === 'render'
|
|
148
|
+
&& renderContext.currentVariant?.id === variant.id
|
|
149
|
+
|
|
150
|
+
if (shouldSync && !renderStateSync) {
|
|
151
|
+
renderStateSync = syncStateBundledAndExternal(variant.state, implicitState())
|
|
152
|
+
}
|
|
153
|
+
else if (!shouldSync && renderStateSync) {
|
|
154
|
+
renderStateSync.stop()
|
|
155
|
+
renderStateSync = null
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (mountVariant.value) {
|
|
160
|
+
updateVariant(mountVariant.value)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
onBeforeUnmount(() => {
|
|
164
|
+
renderStateSync?.stop()
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
renderContext,
|
|
169
|
+
renderVariantSlot,
|
|
170
|
+
resolveVariant,
|
|
171
|
+
syncRenderVariantState,
|
|
172
|
+
updateVariant,
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
render() {
|
|
177
|
+
const variant = this.resolveVariant()
|
|
178
|
+
|
|
179
|
+
if (!variant) {
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.updateVariant(variant)
|
|
184
|
+
this.syncRenderVariantState(variant)
|
|
185
|
+
|
|
186
|
+
if (this.renderContext?.mode !== 'render') {
|
|
187
|
+
return null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (this.renderContext.currentVariant?.id !== variant.id) {
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return this.renderVariantSlot(variant)
|
|
195
|
+
},
|
|
196
|
+
})
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { AutoPropComponentDefinition, PropDefinition, Variant } from '@poveste/shared'
|
|
2
|
+
import { applyState } from '@poveste/shared'
|
|
3
|
+
import { getTagName } from '../codegen'
|
|
4
|
+
|
|
5
|
+
export function syncVariantAutoProps(
|
|
6
|
+
variant: Variant,
|
|
7
|
+
vnodes: any,
|
|
8
|
+
externalState: Variant['state'],
|
|
9
|
+
lastSnapshot: string,
|
|
10
|
+
) {
|
|
11
|
+
const defs = scanForAutoProps(vnodes, externalState)
|
|
12
|
+
const snapshot = JSON.stringify(defs)
|
|
13
|
+
|
|
14
|
+
if (!lastSnapshot || lastSnapshot !== snapshot) {
|
|
15
|
+
applyState(variant.state, {
|
|
16
|
+
_hPropDefs: defs,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
if (!variant.state._hPropState) {
|
|
20
|
+
applyState(variant.state, {
|
|
21
|
+
_hPropState: {},
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return snapshot
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function scanForAutoProps(vnodes: any, externalState: Variant['state']) {
|
|
30
|
+
const result: AutoPropComponentDefinition[] = []
|
|
31
|
+
const traversalState = {
|
|
32
|
+
index: 0,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
visitVNodes(vnodes, externalState, traversalState, result)
|
|
36
|
+
|
|
37
|
+
return result.filter(def => def.props.length)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function visitVNodes(vnodes: any, externalState: Variant['state'], traversalState: { index: number }, result: AutoPropComponentDefinition[]) {
|
|
41
|
+
for (const vnode of normalizeVNodes(vnodes)) {
|
|
42
|
+
if (!vnode) continue
|
|
43
|
+
|
|
44
|
+
if (typeof vnode.type === 'object') {
|
|
45
|
+
const index = traversalState.index++
|
|
46
|
+
const propDefs: PropDefinition[] = []
|
|
47
|
+
|
|
48
|
+
for (const key in vnode.type.props) {
|
|
49
|
+
const prop = vnode.type.props[key]
|
|
50
|
+
let types
|
|
51
|
+
let defaultValue
|
|
52
|
+
|
|
53
|
+
if (prop) {
|
|
54
|
+
const rawTypes = Array.isArray(prop.type)
|
|
55
|
+
? prop.type
|
|
56
|
+
: typeof prop === 'function'
|
|
57
|
+
? [prop]
|
|
58
|
+
: [prop.type]
|
|
59
|
+
|
|
60
|
+
types = rawTypes.map((type) => {
|
|
61
|
+
switch (type) {
|
|
62
|
+
case String:
|
|
63
|
+
return 'string'
|
|
64
|
+
case Number:
|
|
65
|
+
return 'number'
|
|
66
|
+
case Boolean:
|
|
67
|
+
return 'boolean'
|
|
68
|
+
case Object:
|
|
69
|
+
return 'object'
|
|
70
|
+
case Array:
|
|
71
|
+
return 'array'
|
|
72
|
+
default:
|
|
73
|
+
return 'unknown'
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
defaultValue = typeof prop.default === 'function'
|
|
78
|
+
? prop.default.toString()
|
|
79
|
+
: prop.default
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
propDefs.push({
|
|
83
|
+
name: key,
|
|
84
|
+
types,
|
|
85
|
+
required: prop?.required,
|
|
86
|
+
default: defaultValue,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
if (externalState?._hPropState?.[index]?.[key] != null) {
|
|
90
|
+
if (!vnode.props) {
|
|
91
|
+
vnode.props = {}
|
|
92
|
+
}
|
|
93
|
+
vnode.props[key] = externalState._hPropState[index][key]
|
|
94
|
+
|
|
95
|
+
if (!vnode.dynamicProps) {
|
|
96
|
+
vnode.dynamicProps = []
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!vnode.dynamicProps.includes(key)) {
|
|
100
|
+
vnode.dynamicProps.push(key)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
result.push({
|
|
106
|
+
name: getTagName(vnode),
|
|
107
|
+
index,
|
|
108
|
+
props: propDefs,
|
|
109
|
+
} as AutoPropComponentDefinition)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (Array.isArray(vnode.children)) {
|
|
113
|
+
visitVNodes(vnode.children, externalState, traversalState, result)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function normalizeVNodes(vnodes: any) {
|
|
119
|
+
if (!Array.isArray(vnodes)) {
|
|
120
|
+
return vnodes == null ? [] : [vnodes]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = []
|
|
124
|
+
|
|
125
|
+
for (const vnode of vnodes) {
|
|
126
|
+
if (Array.isArray(vnode)) {
|
|
127
|
+
result.push(...normalizeVNodes(vnode))
|
|
128
|
+
}
|
|
129
|
+
else if (vnode != null) {
|
|
130
|
+
result.push(vnode)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return result
|
|
135
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
App as _App,
|
|
3
|
+
} from '@poveste/vendors/vue'
|
|
4
|
+
import type {
|
|
5
|
+
App,
|
|
6
|
+
} from 'vue'
|
|
7
|
+
import { components } from '@poveste/controls'
|
|
8
|
+
import {
|
|
9
|
+
createApp as _createApp,
|
|
10
|
+
h as _h,
|
|
11
|
+
reactive as _reactive,
|
|
12
|
+
} from '@poveste/vendors/vue'
|
|
13
|
+
import {
|
|
14
|
+
defineComponent,
|
|
15
|
+
h,
|
|
16
|
+
onBeforeUnmount,
|
|
17
|
+
onBeforeUpdate,
|
|
18
|
+
onMounted,
|
|
19
|
+
onUpdated,
|
|
20
|
+
ref,
|
|
21
|
+
} from 'vue'
|
|
22
|
+
import Story from './Story'
|
|
23
|
+
import Variant from './Variant'
|
|
24
|
+
|
|
25
|
+
export function registerGlobalComponents(app: App) {
|
|
26
|
+
app.component('Story', Story)
|
|
27
|
+
|
|
28
|
+
app.component('Variant', Variant)
|
|
29
|
+
|
|
30
|
+
for (const key in components) {
|
|
31
|
+
app.component(key, wrapControlComponent(components[key]))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function wrapControlComponent(controlComponent) {
|
|
36
|
+
return defineComponent({
|
|
37
|
+
name: controlComponent.name,
|
|
38
|
+
inheritAttrs: false,
|
|
39
|
+
setup(props, { attrs }) {
|
|
40
|
+
const el = ref<HTMLDivElement>()
|
|
41
|
+
const slotEl = ref<HTMLDivElement>()
|
|
42
|
+
|
|
43
|
+
// Attrs
|
|
44
|
+
|
|
45
|
+
const state = _reactive({})
|
|
46
|
+
|
|
47
|
+
function applyState(data) {
|
|
48
|
+
Object.assign(state, data)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
applyState(attrs)
|
|
52
|
+
onBeforeUpdate(() => {
|
|
53
|
+
applyState(attrs)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Slots
|
|
57
|
+
|
|
58
|
+
let newSlotCalls = []
|
|
59
|
+
const slotCalls = ref([])
|
|
60
|
+
|
|
61
|
+
function moveSlotContent() {
|
|
62
|
+
slotCalls.value.forEach((props, index) => {
|
|
63
|
+
const renderedEl = slotEl.value.querySelector(`[renderslotid="${index}"]`)
|
|
64
|
+
if (!renderedEl) return
|
|
65
|
+
const targetEl = el.value.querySelector(`[slotid="${index}"]`)
|
|
66
|
+
while (targetEl.firstChild) {
|
|
67
|
+
targetEl.removeChild(targetEl.lastChild)
|
|
68
|
+
}
|
|
69
|
+
targetEl.appendChild(renderedEl)
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// App
|
|
74
|
+
|
|
75
|
+
let app: _App
|
|
76
|
+
|
|
77
|
+
onMounted(() => {
|
|
78
|
+
app = _createApp({
|
|
79
|
+
mounted() {
|
|
80
|
+
slotCalls.value = newSlotCalls
|
|
81
|
+
newSlotCalls = []
|
|
82
|
+
},
|
|
83
|
+
updated() {
|
|
84
|
+
slotCalls.value = newSlotCalls
|
|
85
|
+
newSlotCalls = []
|
|
86
|
+
},
|
|
87
|
+
render() {
|
|
88
|
+
return _h(controlComponent, {
|
|
89
|
+
...state,
|
|
90
|
+
key: 'component',
|
|
91
|
+
}, {
|
|
92
|
+
default: (props) => {
|
|
93
|
+
newSlotCalls.push(props)
|
|
94
|
+
return _h('div', {
|
|
95
|
+
slotId: newSlotCalls.length - 1,
|
|
96
|
+
})
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
app.mount(el.value)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
onUpdated(() => {
|
|
105
|
+
moveSlotContent()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
onBeforeUnmount(() => {
|
|
109
|
+
app.unmount()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
el,
|
|
114
|
+
slotEl,
|
|
115
|
+
slotCalls,
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
render() {
|
|
119
|
+
return [
|
|
120
|
+
h('div', {
|
|
121
|
+
ref: 'el',
|
|
122
|
+
}),
|
|
123
|
+
h('div', {
|
|
124
|
+
ref: 'slotEl',
|
|
125
|
+
}, this.slotCalls.map((props, index) => h('div', {
|
|
126
|
+
renderSlotId: index,
|
|
127
|
+
}, this.$slots.default(props)))),
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { Story, Variant } from '@poveste/shared'
|
|
2
|
+
import type { App, Component, PropType, VNode } from 'vue'
|
|
3
|
+
import type { Vue3StorySetupApi, Vue3StorySetupHandler } from '../../helpers.js'
|
|
4
|
+
// @ts-expect-error virtual module id
|
|
5
|
+
import * as generatedSetup from 'virtual:$histoire-generated-global-setup'
|
|
6
|
+
// @ts-expect-error virtual module id
|
|
7
|
+
import * as setup from 'virtual:$histoire-setup'
|
|
8
|
+
import {
|
|
9
|
+
createApp,
|
|
10
|
+
defineComponent,
|
|
11
|
+
h,
|
|
12
|
+
Suspense,
|
|
13
|
+
} from 'vue'
|
|
14
|
+
import { registerGlobalComponents } from './global-components.js'
|
|
15
|
+
import { type PreviewRenderContext, provideRenderContext } from './render-context.js'
|
|
16
|
+
import { RouterLinkStub } from './RouterLinkStub'
|
|
17
|
+
|
|
18
|
+
interface PreviewHostOptions {
|
|
19
|
+
name: string
|
|
20
|
+
el: HTMLElement
|
|
21
|
+
getStory: () => Story
|
|
22
|
+
getVariant: () => Variant | null
|
|
23
|
+
renderContext: PreviewRenderContext
|
|
24
|
+
wrapInDiv?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const PreviewHostRoot = defineComponent({
|
|
28
|
+
name: 'PreviewHostRoot',
|
|
29
|
+
|
|
30
|
+
props: {
|
|
31
|
+
story: {
|
|
32
|
+
type: Object as PropType<Story>,
|
|
33
|
+
required: true,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
renderContext: {
|
|
37
|
+
type: Object as PropType<PreviewRenderContext>,
|
|
38
|
+
required: true,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
setup(props) {
|
|
43
|
+
provideRenderContext(props.renderContext)
|
|
44
|
+
|
|
45
|
+
return () => h(props.story.file.component, {
|
|
46
|
+
story: props.story,
|
|
47
|
+
})
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
export function createPreviewHost(options: PreviewHostOptions) {
|
|
52
|
+
let app: App = null
|
|
53
|
+
let target: HTMLDivElement = null
|
|
54
|
+
|
|
55
|
+
async function mount() {
|
|
56
|
+
const wrappers: Component[] = []
|
|
57
|
+
|
|
58
|
+
target = document.createElement('div')
|
|
59
|
+
options.el.appendChild(target)
|
|
60
|
+
|
|
61
|
+
app = createApp({
|
|
62
|
+
name: options.name,
|
|
63
|
+
|
|
64
|
+
render: () => {
|
|
65
|
+
let vnode: VNode = h(PreviewHostRoot, {
|
|
66
|
+
story: options.getStory(),
|
|
67
|
+
renderContext: options.renderContext,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
for (const wrapper of wrappers) {
|
|
71
|
+
const child = vnode
|
|
72
|
+
vnode = h(wrapper, {
|
|
73
|
+
story: options.getStory(),
|
|
74
|
+
variant: options.getVariant(),
|
|
75
|
+
}, () => child)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (options.wrapInDiv) {
|
|
79
|
+
vnode = h('div', vnode)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return h(Suspense, {}, vnode)
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
registerGlobalComponents(app)
|
|
87
|
+
app.component('RouterLink', RouterLinkStub)
|
|
88
|
+
|
|
89
|
+
const setupApi: Vue3StorySetupApi = {
|
|
90
|
+
app,
|
|
91
|
+
story: options.getStory(),
|
|
92
|
+
variant: options.getVariant(),
|
|
93
|
+
addWrapper: (wrapper) => {
|
|
94
|
+
wrappers.unshift(wrapper)
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await runSetupHooks(setupApi)
|
|
99
|
+
|
|
100
|
+
app.mount(target)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function unmount() {
|
|
104
|
+
app?.unmount()
|
|
105
|
+
app = null
|
|
106
|
+
|
|
107
|
+
if (target) {
|
|
108
|
+
target.parentNode?.removeChild(target)
|
|
109
|
+
target = null
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function forceUpdate() {
|
|
114
|
+
app?._instance?.proxy?.$forceUpdate()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
mount,
|
|
119
|
+
unmount,
|
|
120
|
+
forceUpdate,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function runSetupHooks(setupApi: Vue3StorySetupApi) {
|
|
125
|
+
if (typeof generatedSetup?.setupVue3 === 'function') {
|
|
126
|
+
const setupFn = generatedSetup.setupVue3 as Vue3StorySetupHandler
|
|
127
|
+
await setupFn(setupApi)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof setup?.setupVue3 === 'function') {
|
|
131
|
+
const setupFn = setup.setupVue3 as Vue3StorySetupHandler
|
|
132
|
+
await setupFn(setupApi)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (typeof setupApi.variant?.setupApp === 'function') {
|
|
136
|
+
const setupFn = setupApi.variant.setupApp as Vue3StorySetupHandler
|
|
137
|
+
await setupFn(setupApi)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Variant } from '@poveste/shared'
|
|
2
|
+
import type { InjectionKey } from 'vue'
|
|
3
|
+
import { inject, provide } from 'vue'
|
|
4
|
+
|
|
5
|
+
export interface PreviewRenderContext {
|
|
6
|
+
mode: 'mount' | 'render'
|
|
7
|
+
slotName: string
|
|
8
|
+
currentVariant: Variant | null
|
|
9
|
+
externalState: Variant['state'] | null
|
|
10
|
+
nextVariantIndex: {
|
|
11
|
+
value: number
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const previewRenderContextKey: InjectionKey<PreviewRenderContext> = Symbol('histoire-preview-render-context')
|
|
16
|
+
|
|
17
|
+
export function provideRenderContext(value: PreviewRenderContext) {
|
|
18
|
+
provide(previewRenderContextKey, value)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useRenderContext() {
|
|
22
|
+
return inject(previewRenderContextKey, null)
|
|
23
|
+
}
|