UNPKG

@poupe/vue

Version:

Vue component library for Poupe UI framework with theme customization and accessibility support

586 lines (585 loc) 23.3 kB
import "@poupe/vue/story-viewer/index.css"; import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, guardReactiveProps, h, normalizeClass, normalizeProps, onMounted, onUnmounted, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, watch, withCtx } from "vue"; import { Icon } from "@iconify/vue"; var code_block_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "code-block", props: { code: { type: String, required: true }, language: { type: String, required: false, default: "vue" } }, setup(__props, { expose: __expose }) { __expose(); const props = __props; const copied = ref(false); const formattedCode = computed(() => props.code.trim()); async function copyToClipboard() { try { await globalThis.navigator.clipboard.writeText(formattedCode.value); copied.value = true; globalThis.setTimeout(() => { copied.value = false; }, 2e3); } catch (error) { globalThis.console.error("Failed to copy:", error); } } const __returned__ = { props, copied, formattedCode, copyToClipboard, get Icon() { return Icon; } }; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); var export_helper_default = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) target[key] = val; return target; }; const _hoisted_1$3 = { class: "story-code-block surface-container-high on-surface rounded-lg overflow-hidden" }; const _hoisted_2$3 = { class: "flex items-center justify-between px-3 py-2 border-b border-outline-variant" }; const _hoisted_3$3 = { class: "text-xs on-surface-variant" }; const _hoisted_4$3 = ["title"]; const _hoisted_5$3 = { class: "overflow-x-auto p-4 text-sm font-mono" }; function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("div", _hoisted_1$3, [createElementVNode("div", _hoisted_2$3, [createElementVNode("span", _hoisted_3$3, toDisplayString($props.language || "code"), 1), createElementVNode("button", { class: "p-1 -mr-1 surface-container-highest on-surface rounded hover:surface-container transition-colors", title: $setup.copied ? "Copied!" : "Copy code", onClick: $setup.copyToClipboard }, [createVNode($setup["Icon"], { icon: $setup.copied ? "mdi:check" : "mdi:content-copy", class: "w-4 h-4" }, null, 8, ["icon"])], 8, _hoisted_4$3)]), createElementVNode("pre", _hoisted_5$3, [createElementVNode("code", null, toDisplayString($setup.formattedCode), 1)])]); } var code_block_default = /* @__PURE__ */ export_helper_default(code_block_vue_vue_type_script_setup_true_lang_default, [ ["render", _sfc_render$6], ["__scopeId", "data-v-39814206"], ["__file", "/home/runner/work/poupe/poupe/packages/@poupe-vue/src/components/story/code-block.vue"] ]); var showcase_item_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "showcase-item", props: { code: { type: null, required: false, default: void 0 }, language: { type: String, required: false, default: "vue" }, title: { type: null, required: false, default: void 0 } }, setup(__props, { expose: __expose }) { __expose(); const __returned__ = { showCode: ref(false), get Icon() { return Icon; }, StoryCodeBlock: code_block_default }; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); const _hoisted_1$2 = { class: "story-showcase-item" }; const _hoisted_2$2 = { key: 0, class: "mb-2 flex items-center justify-between" }; const _hoisted_3$2 = { key: 0, class: "text-sm font-medium on-surface-variant" }; const _hoisted_4$2 = ["title"]; const _hoisted_5$2 = { class: "story-showcase-content" }; const _hoisted_6$1 = { key: 1, class: "mt-4" }; function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("div", _hoisted_1$2, [ $props.title || $props.code ? (openBlock(), createElementBlock("div", _hoisted_2$2, [$props.title ? (openBlock(), createElementBlock("h4", _hoisted_3$2, toDisplayString($props.title), 1)) : createCommentVNode("v-if", true), $props.code ? (openBlock(), createElementBlock("button", { key: 1, class: "ml-auto p-1.5 -mr-1.5 surface-container-highest on-surface rounded hover:surface-container transition-colors", title: $setup.showCode ? "Hide code" : "Show code", onClick: _cache[0] || (_cache[0] = ($event) => $setup.showCode = !$setup.showCode) }, [createVNode($setup["Icon"], { icon: $setup.showCode ? "mdi:code-off" : "mdi:code-tags", class: "w-4 h-4" }, null, 8, ["icon"])], 8, _hoisted_4$2)) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true), createElementVNode("div", _hoisted_5$2, [renderSlot(_ctx.$slots, "default")]), $setup.showCode && $props.code ? (openBlock(), createElementBlock("div", _hoisted_6$1, [createVNode($setup["StoryCodeBlock"], { code: $props.code, language: $props.language }, null, 8, ["code", "language"])])) : createCommentVNode("v-if", true) ]); } var showcase_item_default = /* @__PURE__ */ export_helper_default(showcase_item_vue_vue_type_script_setup_true_lang_default, [["render", _sfc_render$5], ["__file", "/home/runner/work/poupe/poupe/packages/@poupe-vue/src/components/story/showcase-item.vue"]]); function generateCode(story, options = {}) { const { componentPrefix = "P" } = options; if (!story.component) return ""; const componentName = getComponentName(story.component, componentPrefix); const propsString = generatePropsString(story.props || {}); const slotsString = story.slots ? generateSlotsString(story.slots) : ""; let componentCode = slotsString ? `<${componentName}${propsString}>\n${slotsString}\n</${componentName}>` : `<${componentName}${propsString} />`; if (story.wrapperClass) componentCode = `<div class="${story.wrapperClass}">${componentCode}</div>`; return componentCode; } function getComponentName(component, prefix) { if (typeof component === "string") return component; const name = component.name || component.__name; if (!name || typeof name !== "string") return "Component"; return name.startsWith(prefix) ? name : `${prefix}${name}`; } function generatePropsString(props) { const entries = Object.entries(props).filter(([, value]) => value !== void 0 && value !== null); if (entries.length === 0) return ""; return " " + entries.map(([key, value]) => { if (value === true) return key; if (value === false) return `:${key}="false"`; if (typeof value === "string") return `${key}="${value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;")}"`; return `:${key}="${JSON.stringify(value).replaceAll("\"", "&quot;")}"`; }).join(" "); } function generateSlotsString(slots) { return Object.entries(slots).map(([name, content]) => { const slotContent = typeof content === "function" ? "Dynamic content" : content; return name === "default" ? ` ${slotContent}` : ` <template #${name}>\n ${slotContent}\n </template>`; }).join("\n"); } function createStory(config) { return config; } function createStoryGroup(title, description, stories) { return { title, description, stories }; } var renderer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "renderer", props: { options: { type: null, required: false, default: void 0 }, story: { type: Object, required: true } }, setup(__props, { expose: __expose }) { __expose(); const props = __props; const __returned__ = { props, code: computed(() => generateCode(props.story, props.options)), componentToRender: computed(() => { if (!props.story.component) return () => h("div", { class: "text-on-surface-variant" }, "No preview available"); const slots = {}; if (props.story.slots) for (const [name, content] of Object.entries(props.story.slots)) slots[name] = typeof content === "function" ? content : () => content; const component = () => h(props.story.component, props.story.props || {}, slots); if (props.story.wrapperClass) return () => h("div", { class: props.story.wrapperClass }, [component()]); return component; }), StoryShowcaseItem: showcase_item_default }; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createBlock($setup["StoryShowcaseItem"], { title: $props.story.title, code: $setup.code, language: "vue" }, { default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent($setup.componentToRender)))]), _: 1 }, 8, ["title", "code"]); } var renderer_default = /* @__PURE__ */ export_helper_default(renderer_vue_vue_type_script_setup_true_lang_default, [["render", _sfc_render$4], ["__file", "/home/runner/work/poupe/poupe/packages/@poupe-vue/src/components/story/renderer.vue"]]); var section_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "section", props: { description: { type: String, required: false }, title: { type: String, required: true } }, setup(__props, { expose: __expose }) { __expose(); const __returned__ = {}; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); const _hoisted_1$1 = { class: "mb-8" }; const _hoisted_2$1 = { class: "mb-4" }; const _hoisted_3$1 = { class: "text-2xl font-bold text-on-surface" }; const _hoisted_4$1 = { key: 0, class: "text-on-surface-variant mt-1" }; const _hoisted_5$1 = { class: "space-y-4" }; function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("section", _hoisted_1$1, [createElementVNode("header", _hoisted_2$1, [createElementVNode("h2", _hoisted_3$1, toDisplayString($props.title), 1), $props.description ? (openBlock(), createElementBlock("p", _hoisted_4$1, toDisplayString($props.description), 1)) : createCommentVNode("v-if", true)]), createElementVNode("div", _hoisted_5$1, [renderSlot(_ctx.$slots, "default")])]); } var section_default = /* @__PURE__ */ export_helper_default(section_vue_vue_type_script_setup_true_lang_default, [["render", _sfc_render$3], ["__file", "/home/runner/work/poupe/poupe/packages/@poupe-vue/src/components/story/section.vue"]]); var showcase_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "showcase", props: { center: { type: Boolean, required: false, default: false }, gap: { type: String, required: false, default: "base" }, wrap: { type: Boolean, required: false, default: true } }, setup(__props, { expose: __expose }) { __expose(); const __returned__ = {}; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("div", { class: normalizeClass(["flex", { "flex-wrap": $props.wrap, "items-center": $props.center, "gap-2": $props.gap === "xs", "gap-3": $props.gap === "sm", "gap-4": $props.gap === "base", "gap-6": $props.gap === "lg", "gap-8": $props.gap === "xl" }]) }, [renderSlot(_ctx.$slots, "default")], 2); } var showcase_default = /* @__PURE__ */ export_helper_default(showcase_vue_vue_type_script_setup_true_lang_default, [["render", _sfc_render$2], ["__file", "/home/runner/work/poupe/poupe/packages/@poupe-vue/src/components/story/showcase.vue"]]); var group_renderer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "group-renderer", props: { group: { type: Object, required: true }, options: { type: null, required: false, default: void 0 }, showcaseProps: { type: Object, required: false, default: () => ({ gap: "lg" }) } }, setup(__props, { expose: __expose }) { __expose(); const __returned__ = { StoryRenderer: renderer_default, StorySection: section_default, StoryShowcase: showcase_default }; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createBlock($setup["StorySection"], { title: $props.group.title, description: $props.group.description }, { default: withCtx(() => [createVNode($setup["StoryShowcase"], normalizeProps(guardReactiveProps($props.showcaseProps)), { default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList($props.group.stories, (story, index) => { return openBlock(), createBlock($setup["StoryRenderer"], { key: `${story.title}-${index}`, story, options: $props.options }, null, 8, ["story", "options"]); }), 128))]), _: 1 }, 16)]), _: 1 }, 8, ["title", "description"]); } var group_renderer_default = /* @__PURE__ */ export_helper_default(group_renderer_vue_vue_type_script_setup_true_lang_default, [["render", _sfc_render$1], ["__file", "/home/runner/work/poupe/poupe/packages/@poupe-vue/src/components/story/group-renderer.vue"]]); var viewer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "viewer", props: { defaultStory: { type: String, required: false, default: void 0 }, stories: { type: Array, required: true }, useRouter: { type: Object, required: false, default: void 0 } }, setup(__props, { expose: __expose }) { __expose(); const props = __props; const createSlug = (name) => name.toLowerCase().replaceAll(/\s+/g, "-"); const getStoryFromSlug = (slug) => { return props.stories.find((s) => createSlug(s.name) === slug)?.name || ""; }; const navigation = computed(() => props.useRouter ? { getCurrentStory: () => { const storyParam = props.useRouter.route.query.story; if (storyParam) { const storyName = getStoryFromSlug(storyParam); if (storyName) return storyName; } return props.defaultStory || props.stories[0]?.name || ""; }, setStory: (storyName) => { const slug = createSlug(storyName); props.useRouter.router.push({ query: { ...props.useRouter.route.query, story: slug } }); }, init: () => { if (!props.useRouter.route.query.story && currentStory.value) { const slug = createSlug(currentStory.value.name); props.useRouter.router.replace({ query: { ...props.useRouter.route.query, story: slug } }); } } } : { getCurrentStory: () => { if (globalThis.location !== void 0) { const hash = globalThis.location.hash.slice(1); if (hash) { const storyName = getStoryFromSlug(hash); if (storyName) return storyName; } } return props.defaultStory || props.stories[0]?.name || ""; }, setStory: (storyName) => { if (globalThis.location !== void 0) globalThis.location.hash = createSlug(storyName); }, init: () => { if (globalThis.location !== void 0 && !globalThis.location.hash && currentStory.value) globalThis.location.hash = createSlug(currentStory.value.name); } }); const getInitialStoryName = () => { if (globalThis.location === void 0 || !globalThis.location.hash) return props.defaultStory || props.stories[0]?.name || ""; return navigation.value.getCurrentStory(); }; const currentStoryName = ref(getInitialStoryName()); const currentStory = computed(() => { return props.stories.find((s) => s.name === currentStoryName.value); }); watch(() => [props.stories, props.defaultStory], () => { const newStory = navigation.value.getCurrentStory(); if (newStory !== currentStoryName.value) currentStoryName.value = newStory; }); const selectStory = (storyName) => { currentStoryName.value = storyName; navigation.value.setStory(storyName); }; let hashChangeListener; if (props.useRouter) watch(() => props.useRouter.route.query.story, (newStory) => { if (newStory) { const storyName = getStoryFromSlug(newStory); if (storyName && storyName !== currentStoryName.value) currentStoryName.value = storyName; } }); else { onMounted(() => { hashChangeListener = () => { const newStoryName = navigation.value.getCurrentStory(); if (newStoryName !== currentStoryName.value) currentStoryName.value = newStoryName; }; if (typeof globalThis.addEventListener === "function") globalThis.addEventListener("hashchange", hashChangeListener); }); onUnmounted(() => { if (hashChangeListener && typeof globalThis.removeEventListener === "function") globalThis.removeEventListener("hashchange", hashChangeListener); }); } onMounted(() => { if (props.useRouter || globalThis.location !== void 0) navigation.value.init(); }); const isMobileMenuOpen = ref(false); provide("storyViewer", { currentStory: currentStoryName }); const __returned__ = { props, createSlug, getStoryFromSlug, navigation, getInitialStoryName, currentStoryName, currentStory, selectStory, get hashChangeListener() { return hashChangeListener; }, set hashChangeListener(v) { hashChangeListener = v; }, isMobileMenuOpen }; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); const _hoisted_1 = { class: "flex flex-col min-h-screen" }; const _hoisted_2 = { class: "lg:hidden surface-container-high shadow-z1 sticky top-0 z-20" }; const _hoisted_3 = { class: "p-4 flex items-center justify-between" }; const _hoisted_4 = { class: "text-lg font-bold" }; const _hoisted_5 = ["aria-label"]; const _hoisted_6 = { class: "w-6 h-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }; const _hoisted_7 = { key: 0, "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M4 6h16M4 12h16M4 18h16" }; const _hoisted_8 = { key: 1, "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M6 18L18 6M6 6l12 12" }; const _hoisted_9 = { class: "flex flex-1" }; const _hoisted_10 = { class: "hidden lg:block p-4 border-b border-outline-variant" }; const _hoisted_11 = { class: "text-xl font-bold" }; const _hoisted_12 = { class: "flex-1 p-2 overflow-y-auto" }; const _hoisted_13 = ["onClick"]; const _hoisted_14 = { class: "flex-1 flex flex-col lg:ml-0" }; const _hoisted_15 = { key: 0, class: "surface-container p-4 lg:p-6 border-b border-outline-variant" }; const _hoisted_16 = { class: "text-xl lg:text-2xl font-bold mb-1" }; const _hoisted_17 = { key: 0, class: "text-on-surface-variant text-sm lg:text-base" }; const _hoisted_18 = { class: "flex-1 surface overflow-y-auto" }; const _hoisted_19 = { class: "container m-auto max-w-6xl p-4 lg:p-6" }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("div", _hoisted_1, [ createCommentVNode(" Mobile Header "), createElementVNode("div", _hoisted_2, [createElementVNode("div", _hoisted_3, [createElementVNode("h1", _hoisted_4, [renderSlot(_ctx.$slots, "title", {}, () => [_cache[2] || (_cache[2] = createTextVNode(" Component Stories ", -1))])]), createElementVNode("button", { class: "p-2 rounded-md hover:bg-surface-container-highest", "aria-label": $setup.isMobileMenuOpen ? "Close menu" : "Open menu", onClick: _cache[0] || (_cache[0] = ($event) => $setup.isMobileMenuOpen = !$setup.isMobileMenuOpen) }, [(openBlock(), createElementBlock("svg", _hoisted_6, [!$setup.isMobileMenuOpen ? (openBlock(), createElementBlock("path", _hoisted_7)) : (openBlock(), createElementBlock("path", _hoisted_8))]))], 8, _hoisted_5)])]), createCommentVNode(" Mobile Menu Overlay "), $setup.isMobileMenuOpen ? (openBlock(), createElementBlock("div", { key: 0, class: "lg:hidden fixed inset-0 z-10 bg-scrim/50", onClick: _cache[1] || (_cache[1] = ($event) => $setup.isMobileMenuOpen = false) })) : createCommentVNode("v-if", true), createCommentVNode(" Main Layout "), createElementVNode("div", _hoisted_9, [ createCommentVNode(" Sidebar Navigation "), createElementVNode("div", { class: normalizeClass([ "fixed lg:static inset-y-0 left-0 z-20 w-64", "surface-container-high shadow-z2 lg:shadow-z1", "transform transition-transform duration-200 lg:translate-x-0", "flex flex-col h-full lg:h-auto", $setup.isMobileMenuOpen ? "translate-x-0" : "-translate-x-full" ]) }, [ createElementVNode("div", _hoisted_10, [createElementVNode("h1", _hoisted_11, [renderSlot(_ctx.$slots, "title", {}, () => [_cache[3] || (_cache[3] = createTextVNode(" Component Stories ", -1))])])]), createCommentVNode(" Story List "), createElementVNode("nav", _hoisted_12, [(openBlock(true), createElementBlock(Fragment, null, renderList($props.stories, (story) => { return openBlock(), createElementBlock("button", { key: story.name, class: normalizeClass([ "w-full text-left px-4 py-2 mb-1 rounded-md transition-colors", "text-sm font-medium", $setup.currentStoryName === story.name ? "bg-primary text-on-primary" : "hover:bg-surface-container-highest text-on-surface" ]), onClick: ($event) => { $setup.selectStory(story.name); $setup.isMobileMenuOpen = false; } }, toDisplayString(story.name), 11, _hoisted_13); }), 128))]) ], 2), createCommentVNode(" Main Content "), createElementVNode("div", _hoisted_14, [ createCommentVNode(" Story Header "), $setup.currentStory ? (openBlock(), createElementBlock("div", _hoisted_15, [createElementVNode("h2", _hoisted_16, toDisplayString($setup.currentStory.name), 1), $setup.currentStory.description ? (openBlock(), createElementBlock("p", _hoisted_17, toDisplayString($setup.currentStory.description), 1)) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true), createCommentVNode(" Story Content "), createElementVNode("div", _hoisted_18, [createElementVNode("div", _hoisted_19, [renderSlot(_ctx.$slots, "story", { story: $setup.currentStory }, () => [$setup.currentStory ? (openBlock(), createBlock(resolveDynamicComponent($setup.currentStory?.component), { key: 0 })) : createCommentVNode("v-if", true)])])]) ]) ]) ]); } var viewer_default = /* @__PURE__ */ export_helper_default(viewer_vue_vue_type_script_setup_true_lang_default, [["render", _sfc_render], ["__file", "/home/runner/work/poupe/poupe/packages/@poupe-vue/src/components/story/viewer.vue"]]); export { code_block_default as StoryCodeBlock, group_renderer_default as StoryGroupRenderer, renderer_default as StoryRenderer, section_default as StorySection, showcase_default as StoryShowcase, showcase_item_default as StoryShowcaseItem, viewer_default as StoryViewer, createStory, createStoryGroup, generateCode }; //# sourceMappingURL=index.mjs.map