@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,10 @@
|
|
|
1
|
+
import type { Story, Variant } from '@poveste/shared';
|
|
2
|
+
import type { App, Component } from 'vue';
|
|
3
|
+
export interface Vue3StorySetupApi {
|
|
4
|
+
app: App;
|
|
5
|
+
story?: Story;
|
|
6
|
+
variant?: Variant;
|
|
7
|
+
addWrapper: (wrapper: Component) => void;
|
|
8
|
+
}
|
|
9
|
+
export type Vue3StorySetupHandler = (api: Vue3StorySetupApi) => Promise<void> | void;
|
|
10
|
+
export declare function defineSetupVue3(handler: Vue3StorySetupHandler): Vue3StorySetupHandler;
|
package/dist/helpers.js
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './helpers.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './helpers.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import generateStoryCommand from './commands/generate-story.server.js';
|
|
2
|
+
import { listComponentFiles } from './util/list-components.js';
|
|
3
|
+
export function HstVue() {
|
|
4
|
+
return {
|
|
5
|
+
name: '@poveste/plugin-vue',
|
|
6
|
+
defaultConfig() {
|
|
7
|
+
return {
|
|
8
|
+
supportMatch: [
|
|
9
|
+
{
|
|
10
|
+
id: 'vue',
|
|
11
|
+
patterns: ['**/*.vue'],
|
|
12
|
+
pluginIds: ['vue3'],
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
config() {
|
|
18
|
+
return {
|
|
19
|
+
vite: {
|
|
20
|
+
plugins: [
|
|
21
|
+
{
|
|
22
|
+
name: 'histoire-plugin-vue',
|
|
23
|
+
enforce: 'post',
|
|
24
|
+
transform(code, id) {
|
|
25
|
+
// Remove vue warnings about unknown components
|
|
26
|
+
if (this.meta.histoire?.isCollecting && id.endsWith('.vue')) {
|
|
27
|
+
return `const _stubComponent = (name) => ['Story','Variant'].some(validName => name.toLowerCase() === validName.toLowerCase()) ? _resolveComponent(name) : ({ render: () => null });${code?.replaceAll('_resolveComponent(', '_stubComponent(') ?? ''}`;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
supportPlugin: {
|
|
36
|
+
id: 'vue3',
|
|
37
|
+
moduleName: '@poveste/plugin-vue',
|
|
38
|
+
setupFn: 'setupVue3',
|
|
39
|
+
importStoriesPrepend: `import { defineAsyncComponent as defineAsyncComponentVue3 } from 'vue'`,
|
|
40
|
+
importStoryComponent: (file, index) => `const Comp${index} = defineAsyncComponentVue3(() => import(${JSON.stringify(file.moduleId)}))`,
|
|
41
|
+
},
|
|
42
|
+
commands: [
|
|
43
|
+
generateStoryCommand,
|
|
44
|
+
],
|
|
45
|
+
async onDevEvent(api) {
|
|
46
|
+
switch (api.event) {
|
|
47
|
+
case 'listVueComponents': {
|
|
48
|
+
return listComponentFiles(api.payload.search, api.getConfig().storyMatch);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export * from './helpers.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function listComponentFiles(search?: string, ignore?: string[], limit?: number): Promise<string[]>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { globby } from 'globby';
|
|
2
|
+
export async function listComponentFiles(search = '', ignore = [], limit = 10) {
|
|
3
|
+
let files = await globby('**/*.vue', {
|
|
4
|
+
gitignore: true,
|
|
5
|
+
ignore: [
|
|
6
|
+
'node_modules',
|
|
7
|
+
...ignore,
|
|
8
|
+
],
|
|
9
|
+
});
|
|
10
|
+
if (search) {
|
|
11
|
+
const searchText = search.toLowerCase();
|
|
12
|
+
files = files.filter(file => file.toLowerCase().includes(searchText));
|
|
13
|
+
}
|
|
14
|
+
return files.slice(0, limit);
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@poveste/plugin-vue",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Poveste plugin for Vue.js support",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Sorin Gitlan"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"url": "https://github.com/poveste-dev/poveste.git",
|
|
12
|
+
"type": "git",
|
|
13
|
+
"directory": "packages/poveste-plugin-vue"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
"./client": {
|
|
20
|
+
"types": "./dist/client/client.d.ts",
|
|
21
|
+
"default": "./dist/bundled/client/client.js"
|
|
22
|
+
},
|
|
23
|
+
"./collect": {
|
|
24
|
+
"types": "./dist/client/server.d.ts",
|
|
25
|
+
"default": "./dist/bundled/client/server.js"
|
|
26
|
+
},
|
|
27
|
+
"./components": {
|
|
28
|
+
"types": "./components.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./client-dev": {
|
|
31
|
+
"default": "./src/client/client.ts"
|
|
32
|
+
},
|
|
33
|
+
"./collect-dev": {
|
|
34
|
+
"default": "./src/client/server.ts"
|
|
35
|
+
},
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.node.d.ts",
|
|
38
|
+
"node": "./dist/index.node.js",
|
|
39
|
+
"default": "./dist/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./*": "./*"
|
|
42
|
+
},
|
|
43
|
+
"main": "dist/index.js",
|
|
44
|
+
"types": "dist/index.node.d.ts",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"vue": "^3.5.26",
|
|
47
|
+
"poveste": "^0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"change-case": "^5.4.4",
|
|
51
|
+
"globby": "^14.0.2",
|
|
52
|
+
"launch-editor": "^2.9.1",
|
|
53
|
+
"pathe": "^1.1.2",
|
|
54
|
+
"@poveste/controls": "^0.1.0",
|
|
55
|
+
"@poveste/vendors": "^0.1.0",
|
|
56
|
+
"@poveste/shared": "^0.1.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^22.10.1",
|
|
60
|
+
"concurrently": "^9.1.0",
|
|
61
|
+
"typescript": "5.6.3",
|
|
62
|
+
"vite": "^7.3.0",
|
|
63
|
+
"vue": "^3.5.26",
|
|
64
|
+
"poveste": "0.1.0"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "rimraf dist && vite build && tsc -d -P tsconfig.build.json && pnpm run build:types",
|
|
68
|
+
"build:types": "tsc --declaration --emitDeclarationOnly",
|
|
69
|
+
"watch": "concurrently \"vite build --watch\" \"tsc -d -P tsconfig.build.json --watch\" \"pnpm run build:types --watch\""
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Story } from '@poveste/shared'
|
|
2
|
+
import type {
|
|
3
|
+
PropType as _PropType,
|
|
4
|
+
} from '@poveste/vendors/vue'
|
|
5
|
+
import {
|
|
6
|
+
defineComponent as _defineComponent,
|
|
7
|
+
h as _h,
|
|
8
|
+
onMounted as _onMounted,
|
|
9
|
+
onUnmounted as _onUnmounted,
|
|
10
|
+
ref as _ref,
|
|
11
|
+
watch as _watch,
|
|
12
|
+
} from '@poveste/vendors/vue'
|
|
13
|
+
import { reactive } from 'vue'
|
|
14
|
+
import { createPreviewHost } from './host.js'
|
|
15
|
+
|
|
16
|
+
export default _defineComponent({
|
|
17
|
+
name: 'MountStory',
|
|
18
|
+
|
|
19
|
+
props: {
|
|
20
|
+
story: {
|
|
21
|
+
type: Object as _PropType<Story>,
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
setup(props) {
|
|
27
|
+
const el = _ref<HTMLDivElement>()
|
|
28
|
+
const renderContext = reactive({
|
|
29
|
+
mode: 'mount' as const,
|
|
30
|
+
slotName: 'default',
|
|
31
|
+
currentVariant: null,
|
|
32
|
+
externalState: null,
|
|
33
|
+
nextVariantIndex: {
|
|
34
|
+
value: 0,
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
let host: ReturnType<typeof createPreviewHost>
|
|
38
|
+
|
|
39
|
+
async function mountStory() {
|
|
40
|
+
host = createPreviewHost({
|
|
41
|
+
name: 'MountStorySubApp',
|
|
42
|
+
el: el.value,
|
|
43
|
+
getStory: () => props.story,
|
|
44
|
+
getVariant: () => null,
|
|
45
|
+
renderContext,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
await host.mount()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function unmountStory() {
|
|
52
|
+
host?.unmount()
|
|
53
|
+
host = null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_watch(() => props.story.id, async () => {
|
|
57
|
+
unmountStory()
|
|
58
|
+
await mountStory()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
_onMounted(async () => {
|
|
62
|
+
await mountStory()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
_watch(() => props.story.variants, () => {
|
|
66
|
+
host?.forceUpdate()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
_onUnmounted(() => {
|
|
70
|
+
unmountStory()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
el,
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
render() {
|
|
79
|
+
return _h('div', {
|
|
80
|
+
ref: 'el',
|
|
81
|
+
})
|
|
82
|
+
},
|
|
83
|
+
})
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Story, Variant } from '@poveste/shared'
|
|
2
|
+
import type {
|
|
3
|
+
PropType as _PropType,
|
|
4
|
+
} from '@poveste/vendors/vue'
|
|
5
|
+
import {
|
|
6
|
+
defineComponent as _defineComponent,
|
|
7
|
+
h as _h,
|
|
8
|
+
onBeforeUnmount as _onBeforeUnmount,
|
|
9
|
+
onMounted as _onMounted,
|
|
10
|
+
ref as _ref,
|
|
11
|
+
watch as _watch,
|
|
12
|
+
} from '@poveste/vendors/vue'
|
|
13
|
+
import { reactive } from 'vue'
|
|
14
|
+
import { createPreviewHost } from './host.js'
|
|
15
|
+
import { syncStateBundledAndExternal } from './util.js'
|
|
16
|
+
|
|
17
|
+
export default _defineComponent({
|
|
18
|
+
name: 'RenderStory',
|
|
19
|
+
|
|
20
|
+
props: {
|
|
21
|
+
variant: {
|
|
22
|
+
type: Object as _PropType<Variant>,
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
story: {
|
|
27
|
+
type: Object as _PropType<Story>,
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
slotName: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: 'default',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
emits: {
|
|
38
|
+
ready: () => true,
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
setup(props, { emit }) {
|
|
42
|
+
const sandbox = _ref<HTMLDivElement>()
|
|
43
|
+
let host: ReturnType<typeof createPreviewHost>
|
|
44
|
+
let mounting = false
|
|
45
|
+
|
|
46
|
+
const externalState = reactive<Variant['state']>({})
|
|
47
|
+
let stateSync = syncStateBundledAndExternal(props.variant.state, externalState)
|
|
48
|
+
const renderContext = reactive({
|
|
49
|
+
mode: 'render' as const,
|
|
50
|
+
slotName: props.slotName,
|
|
51
|
+
currentVariant: props.variant,
|
|
52
|
+
externalState,
|
|
53
|
+
nextVariantIndex: {
|
|
54
|
+
value: 0,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
function unmountVariant() {
|
|
59
|
+
host?.unmount()
|
|
60
|
+
host = null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function syncExternalState() {
|
|
64
|
+
stateSync?.stop()
|
|
65
|
+
stateSync = syncStateBundledAndExternal(props.variant.state, externalState)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function mountVariant() {
|
|
69
|
+
if (mounting) return
|
|
70
|
+
mounting = true
|
|
71
|
+
|
|
72
|
+
unmountVariant()
|
|
73
|
+
renderContext.currentVariant = props.variant
|
|
74
|
+
renderContext.slotName = props.slotName
|
|
75
|
+
|
|
76
|
+
host = createPreviewHost({
|
|
77
|
+
name: 'RenderStorySubApp',
|
|
78
|
+
el: sandbox.value,
|
|
79
|
+
getStory: () => props.story,
|
|
80
|
+
getVariant: () => props.variant,
|
|
81
|
+
renderContext,
|
|
82
|
+
wrapInDiv: true,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await host.mount()
|
|
86
|
+
mounting = false
|
|
87
|
+
|
|
88
|
+
emit('ready')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
_onMounted(async () => {
|
|
92
|
+
if (props.variant.configReady) {
|
|
93
|
+
await mountVariant()
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
_watch(() => props.variant, () => {
|
|
98
|
+
renderContext.currentVariant = props.variant
|
|
99
|
+
syncExternalState()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
_watch(() => [props.story.id, props.slotName, props.variant.id, props.variant.configReady] as const, async ([, slotName]) => {
|
|
103
|
+
renderContext.currentVariant = props.variant
|
|
104
|
+
renderContext.slotName = slotName
|
|
105
|
+
|
|
106
|
+
if (props.variant.configReady && !mounting) {
|
|
107
|
+
if (!host) {
|
|
108
|
+
await mountVariant()
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
host.forceUpdate()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
_onBeforeUnmount(() => {
|
|
117
|
+
stateSync?.stop()
|
|
118
|
+
unmountVariant()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
sandbox,
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
render() {
|
|
127
|
+
return _h('div', {
|
|
128
|
+
ref: 'sandbox',
|
|
129
|
+
})
|
|
130
|
+
},
|
|
131
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// From: https://github.com/vuejs/test-utils/blob/main/src/components/RouterLinkStub.ts
|
|
2
|
+
|
|
3
|
+
import { computed, defineComponent, h } from 'vue'
|
|
4
|
+
|
|
5
|
+
// match return type of router.resolve: RouteLocation & { href: string }
|
|
6
|
+
const defaultRoute = {
|
|
7
|
+
path: '/',
|
|
8
|
+
name: undefined as string | undefined,
|
|
9
|
+
redirectedFrom: undefined as object | undefined,
|
|
10
|
+
params: {},
|
|
11
|
+
query: {},
|
|
12
|
+
hash: '',
|
|
13
|
+
fullPath: '/',
|
|
14
|
+
matched: [] as object[],
|
|
15
|
+
meta: {},
|
|
16
|
+
href: '/',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// TODO: Borrow typings from vue-router-next
|
|
20
|
+
export const RouterLinkStub = defineComponent({
|
|
21
|
+
name: 'RouterLinkStub',
|
|
22
|
+
|
|
23
|
+
compatConfig: { MODE: 3 },
|
|
24
|
+
|
|
25
|
+
props: {
|
|
26
|
+
to: {
|
|
27
|
+
type: [String, Object],
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
custom: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: false,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
render() {
|
|
37
|
+
const route = computed(() => defaultRoute)
|
|
38
|
+
// mock reasonable return values to mimic vue-router's useLink
|
|
39
|
+
const children = this.$slots?.default?.({
|
|
40
|
+
route,
|
|
41
|
+
href: computed(() => route.value.href),
|
|
42
|
+
isActive: computed(() => false),
|
|
43
|
+
isExactActive: computed(() => false),
|
|
44
|
+
navigate: async () => {},
|
|
45
|
+
})
|
|
46
|
+
return this.custom ? children : h('a', undefined, children)
|
|
47
|
+
},
|
|
48
|
+
})
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { Story } from '@poveste/shared'
|
|
2
|
+
import type { PropType, VNode } from 'vue'
|
|
3
|
+
import { omitInheritStoryProps } from '@poveste/shared'
|
|
4
|
+
import { cloneVNode, computed, defineComponent, getCurrentInstance, h, isRef, provide, reactive, useAttrs } from 'vue'
|
|
5
|
+
import { useRenderContext } from './render-context.js'
|
|
6
|
+
import Variant from './Variant'
|
|
7
|
+
|
|
8
|
+
export default defineComponent({
|
|
9
|
+
|
|
10
|
+
name: 'Story',
|
|
11
|
+
__histoireType: 'story',
|
|
12
|
+
|
|
13
|
+
inheritAttrs: false,
|
|
14
|
+
|
|
15
|
+
props: {
|
|
16
|
+
initState: {
|
|
17
|
+
type: Function as PropType<() => any | Promise<any>>,
|
|
18
|
+
default: undefined,
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
meta: {
|
|
22
|
+
type: Object as PropType<Story['meta']>,
|
|
23
|
+
default: undefined,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
setup(props) {
|
|
28
|
+
const vm = getCurrentInstance()
|
|
29
|
+
|
|
30
|
+
const attrs = useAttrs() as {
|
|
31
|
+
story: Story
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const renderContext = useRenderContext()
|
|
35
|
+
const story = computed(() => attrs.story)
|
|
36
|
+
provide('story', story)
|
|
37
|
+
|
|
38
|
+
const storyComponent: any = vm.parent
|
|
39
|
+
const implicitState = reactive<Record<string, any>>({})
|
|
40
|
+
|
|
41
|
+
Object.defineProperty(implicitState, '$data', {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
get: () => storyComponent.data,
|
|
45
|
+
set: (value) => {
|
|
46
|
+
Object.assign(storyComponent.data, value)
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
function addImplicitState(source: Record<string, any>, key: string) {
|
|
51
|
+
const value = source[key]
|
|
52
|
+
|
|
53
|
+
if (typeof value === 'function' || (value?.__file) || typeof value?.render === 'function' || typeof value?.setup === 'function') {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Object.defineProperty(implicitState, key, {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
get: () => {
|
|
61
|
+
const currentValue = source[key]
|
|
62
|
+
return isRef(currentValue) ? currentValue.value : currentValue
|
|
63
|
+
},
|
|
64
|
+
set: (newValue) => {
|
|
65
|
+
const currentValue = source[key]
|
|
66
|
+
|
|
67
|
+
if (isRef(currentValue)) {
|
|
68
|
+
currentValue.value = newValue
|
|
69
|
+
}
|
|
70
|
+
else if (currentValue && typeof currentValue === 'object' && newValue && typeof newValue === 'object') {
|
|
71
|
+
Object.assign(currentValue, newValue)
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
source[key] = newValue
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// From `<script setup>`'s `defineExpose`
|
|
81
|
+
for (const key in storyComponent.exposed) {
|
|
82
|
+
addImplicitState(storyComponent.exposed, key)
|
|
83
|
+
}
|
|
84
|
+
// We needs __VUE_PROD_DEVTOOLS__ flag set to `true` to enable `devtoolsRawSetupState`
|
|
85
|
+
for (const key in storyComponent.devtoolsRawSetupState) {
|
|
86
|
+
addImplicitState(storyComponent.devtoolsRawSetupState, key)
|
|
87
|
+
}
|
|
88
|
+
for (const key in storyComponent.data) {
|
|
89
|
+
addImplicitState(storyComponent.data, key)
|
|
90
|
+
}
|
|
91
|
+
provide('implicitState', () => implicitState)
|
|
92
|
+
|
|
93
|
+
function updateStory() {
|
|
94
|
+
if (props.meta) {
|
|
95
|
+
if (!attrs.story.meta) {
|
|
96
|
+
attrs.story.meta = {}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Object.assign(attrs.story.meta, props.meta)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
Object.assign(attrs.story, {
|
|
103
|
+
slots: () => vm.proxy.$slots,
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
renderContext,
|
|
109
|
+
story,
|
|
110
|
+
renderMountStory,
|
|
111
|
+
renderPreviewStory,
|
|
112
|
+
updateStory,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function renderMountStory() {
|
|
116
|
+
let index = 0
|
|
117
|
+
|
|
118
|
+
const applyAttrs = (vnodes: VNode[]) => {
|
|
119
|
+
const result: VNode[] = []
|
|
120
|
+
|
|
121
|
+
for (const vnode of vnodes) {
|
|
122
|
+
// @ts-expect-error custom option
|
|
123
|
+
if (vnode.type?.__histoireType === 'variant') {
|
|
124
|
+
const variant = attrs.story.variants[index]
|
|
125
|
+
index++
|
|
126
|
+
|
|
127
|
+
if (!variant) {
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const nextProps: any = {
|
|
132
|
+
variant,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!vnode.props?.initState && !vnode.props?.['init-state']) {
|
|
136
|
+
nextProps.initState = props.initState
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const attr in vm.proxy.$attrs) {
|
|
140
|
+
if (typeof vnode.props?.[attr] === 'undefined') {
|
|
141
|
+
nextProps[attr] = vm.proxy.$attrs[attr]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const attr in attrs.story) {
|
|
146
|
+
if (!omitInheritStoryProps.includes(attr) && typeof vnode.props?.[attr] === 'undefined') {
|
|
147
|
+
nextProps[attr] = attrs.story[attr]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
result.push(cloneVNode(vnode, nextProps))
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (Array.isArray(vnode.children)) {
|
|
156
|
+
vnode.children = applyAttrs(vnode.children as VNode[])
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
result.push(vnode)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return result
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return applyAttrs(vm.proxy.$slots.default?.() ?? [])
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function renderPreviewStory() {
|
|
169
|
+
renderContext.nextVariantIndex.value = 0
|
|
170
|
+
|
|
171
|
+
const slotProps = {
|
|
172
|
+
state: renderContext.externalState,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const children = []
|
|
176
|
+
|
|
177
|
+
if (renderContext.slotName === 'controls') {
|
|
178
|
+
children.push(...(vm.proxy.$slots.controls?.(slotProps) ?? []))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (renderContext.slotName === 'default' || attrs.story.meta?.hasVariantChildComponents) {
|
|
182
|
+
children.push(...(vm.proxy.$slots.default?.(slotProps) ?? []))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return children
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
render() {
|
|
190
|
+
this.updateStory()
|
|
191
|
+
|
|
192
|
+
const [firstVariant] = this.story.variants
|
|
193
|
+
|
|
194
|
+
if (firstVariant?.id === '_default') {
|
|
195
|
+
return h(Variant, {
|
|
196
|
+
variant: firstVariant,
|
|
197
|
+
initState: this.initState,
|
|
198
|
+
implicit: true,
|
|
199
|
+
...this.$attrs,
|
|
200
|
+
}, this.$slots)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (this.renderContext?.mode === 'render') {
|
|
204
|
+
return this.renderPreviewStory()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return this.renderMountStory()
|
|
208
|
+
},
|
|
209
|
+
})
|