@poupe/vue
Version:
Vue component library for Poupe UI framework with theme customization and accessibility support
586 lines (585 loc) • 23.3 kB
JavaScript
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("&", "&").replaceAll("\"", """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">")}"`;
return `:${key}="${JSON.stringify(value).replaceAll("\"", """)}"`;
}).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