🎨 fix(docs): interactive colors rendering and lint code

This commit is contained in:
Timothy Yin 2024-11-22 01:35:58 +08:00
parent 36818cefa2
commit e62f7590d0
3 changed files with 73 additions and 60 deletions

View File

@ -39,14 +39,13 @@ const props = defineProps({
}, },
}) })
const componentName = props.slug || `Ray${upperFirst(camelCase(route.params.slug[route.params.slug.length - 1]))}` const componentName = props.slug || `Ray${upperFirst(camelCase(route.params.slug[route.params.slug.length - 1]))}`
const componentMeta = await fetchComponentMeta(componentName) const componentMeta = await fetchComponentMeta(componentName)
const componentProps = reactive({ ...props.props }) const componentProps = reactive({ ...props.props })
const customizableOptions = (key: string, schema: { kind: string, type: string, schema: [] }) => { const customizableOptions = (key: string, schema: { kind: string, type: string, schema: [] }) => {
let options: string[] = []; let options: string[] = []
const invalidTypes = ['string', 'array', 'boolean', 'object', 'number', 'Function'] const invalidTypes = ['string', 'array', 'boolean', 'object', 'number', 'Function']
const hasInvalidType = schema?.type?.split('|')?.map(item => item.trim()?.replaceAll('"', ''))?.some(type => invalidTypes.includes(type)) const hasInvalidType = schema?.type?.split('|')?.map(item => item.trim()?.replaceAll('"', ''))?.some(type => invalidTypes.includes(type))
const schemaOptions = Object.values(schema?.schema || {}) const schemaOptions = Object.values(schema?.schema || {})
@ -56,14 +55,14 @@ const customizableOptions = (key: string, schema: { kind: string, type: string,
} }
if (key.toLowerCase() === 'size' && schemaOptions?.length) { if (key.toLowerCase() === 'size' && schemaOptions?.length) {
const baseSizeOrder = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }; const baseSizeOrder = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }
schemaOptions.sort((a: string, b: string) => { schemaOptions.sort((a: string, b: string) => {
const [aBase, aNum] = [(a.match(/[a-z]+/i)?.[0].toLowerCase() || 'xs') as keyof typeof baseSizeOrder, parseInt(a.match(/\d+/)?.[0] || '1')]; const [aBase, aNum] = [(a.match(/[a-z]+/i)?.[0].toLowerCase() || 'xs') as keyof typeof baseSizeOrder, Number.parseInt(a.match(/\d+/)?.[0] || '1')]
const [bBase, bNum] = [(b.match(/[a-z]+/i)?.[0].toLowerCase() || 'xs') as keyof typeof baseSizeOrder, parseInt(b.match(/\d+/)?.[0] || '1')]; const [bBase, bNum] = [(b.match(/[a-z]+/i)?.[0].toLowerCase() || 'xs') as keyof typeof baseSizeOrder, Number.parseInt(b.match(/\d+/)?.[0] || '1')]
return aBase === bBase return aBase === bBase
? (aBase === 'xs' ? bNum - aNum : aNum - bNum) ? (aBase === 'xs' ? bNum - aNum : aNum - bNum)
: baseSizeOrder[aBase] - baseSizeOrder[bBase]; : baseSizeOrder[aBase] - baseSizeOrder[bBase]
}); })
} }
if (schemaOptions?.length > 0 && schema?.kind === 'enum' && !hasInvalidType) { if (schemaOptions?.length > 0 && schema?.kind === 'enum' && !hasInvalidType) {
@ -73,7 +72,7 @@ const customizableOptions = (key: string, schema: { kind: string, type: string,
return options return options
} }
const customizableProps = computed(() => Object.keys(componentProps).map(k => { const customizableProps = computed(() => Object.keys(componentProps).map((k) => {
const prop = componentMeta?.meta?.props?.find((prop: any) => prop.name === k) const prop = componentMeta?.meta?.props?.find((prop: any) => prop.name === k)
const schema = prop?.schema || {} const schema = prop?.schema || {}
const options = customizableOptions(k, schema) const options = customizableOptions(k, schema)
@ -137,15 +136,27 @@ const { data: codeRender, error: codeRenderError } = await useAsyncData(`${compo
</div> </div>
<div v-if="customizableProps.length > 0" class="border-b border-neutral-200 dark:border-neutral-700 flex"> <div v-if="customizableProps.length > 0" class="border-b border-neutral-200 dark:border-neutral-700 flex">
<div v-for="prop in customizableProps" class="px-2 py-0.5 flex flex-col gap-0.5 border-r dark:border-neutral-700"> <div v-for="(prop, k) in customizableProps" :key="k" class="px-2 py-0.5 flex flex-col gap-0.5 border-r dark:border-neutral-700">
<label :for="`${prop.name}-prop`" class="text-sm text-neutral-400">{{ prop.name }}</label> <label :for="`${prop.name}-prop`" class="text-sm text-neutral-400">{{ prop.name }}</label>
<input v-if="prop.type.startsWith('boolean')" type="checkbox" :id="`${prop.name}-prop`" class="mt-1 mb-2" <input
v-model="componentProps[prop.name]" /> v-if="prop.type.startsWith('boolean')"
:id="`${prop.name}-prop`"
v-model="componentProps[prop.name]"
type="checkbox"
class="mt-1 mb-2"
>
<select v-else-if="prop.options.length > 0" :id="`${prop.name}-prop`" v-model="componentProps[prop.name]"> <select v-else-if="prop.options.length > 0" :id="`${prop.name}-prop`" v-model="componentProps[prop.name]">
<option v-for="option in prop.options" :key="option" :value="option">{{ option }}</option> <option v-for="option in prop.options" :key="option" :value="option">
{{ option }}
</option>
</select> </select>
<input v-else type="text" :id="`${prop.name}-prop`" v-model="componentProps[prop.name]" <input
placeholder="type something..." /> v-else
:id="`${prop.name}-prop`"
v-model="componentProps[prop.name]"
type="text"
placeholder="type something..."
>
</div> </div>
</div> </div>

View File

@ -1,39 +1,39 @@
interface ComponentMetaState { interface ComponentMetaState {
[key: string]: any; [key: string]: any
} }
const useComponentsMetaState = () => const useComponentsMetaState = () =>
useState<ComponentMetaState>("components-meta", () => ({})); useState<ComponentMetaState>('components-meta', () => ({}))
export const fetchComponentMeta = async (name: string) => { export const fetchComponentMeta = async (name: string) => {
const state = useComponentsMetaState(); const state = useComponentsMetaState()
if (state.value[name]?.then) { if (state.value[name]?.then) {
await state.value[name]; await state.value[name]
return state.value[name]; return state.value[name]
} }
if (state.value[name]) { if (state.value[name]) {
return state.value[name]; return state.value[name]
} }
if (import.meta.server) { if (import.meta.server) {
const event = useRequestEvent(); const event = useRequestEvent()
if (event && event.node && event.node.res) { if (event && event.node && event.node.res) {
event.node.res.setHeader( event.node.res.setHeader(
"x-nitro-prerender", 'x-nitro-prerender',
[ [
event.node.res.getHeader("x-nitro-prerender"), event.node.res.getHeader('x-nitro-prerender'),
`/api/component-meta/${name}.json`, `/api/component-meta/${name}.json`,
].filter(Boolean) as string[] ].filter(Boolean) as string[],
); )
} }
} }
state.value[name] = $fetch(`/api/component-meta/${name}.json`).then( state.value[name] = $fetch(`/api/component-meta/${name}.json`).then(
(meta) => { (meta) => {
state.value[name] = meta; state.value[name] = meta
} },
); )
await state.value[name]; await state.value[name]
return state.value[name]; return state.value[name]
}; }

View File

@ -1,49 +1,55 @@
import { createResolver } from "@nuxt/kit"; import { createResolver } from '@nuxt/kit'
import defaultTheme from "tailwindcss/defaultTheme"; import defaultTheme from 'tailwindcss/defaultTheme'
import module from "../src/module"; import colors from 'tailwindcss/colors'
import { excludeColors } from "#rayui/utils/colors"; import module from '../src/module'
import colors from "tailwindcss/colors"; import { excludeColors } from '../dist/runtime/utils/colors'
const { resolve } = createResolver(import.meta.url); console.log(excludeColors(colors))
const { resolve } = createResolver(import.meta.url)
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
modules: [ modules: [
"@nuxt/content", '@nuxt/content',
"@nuxt/fonts", '@nuxt/fonts',
"@nuxtjs/color-mode", '@nuxtjs/color-mode',
module, module,
"nuxt-component-meta", 'nuxt-component-meta',
], ],
devtools: { enabled: true }, devtools: { enabled: true },
colorMode: { colorMode: {
preference: "system", preference: 'system',
classSuffix: "", classSuffix: '',
}, },
content: { content: {
highlight: { highlight: {
langs: ["postcss", "mdc", "html", "vue", "ts", "js", "bash"], langs: ['postcss', 'mdc', 'html', 'vue', 'ts', 'js', 'bash'],
}, },
}, },
mdc: { mdc: {
highlight: { highlight: {
theme: { theme: {
light: "material-theme-lighter", light: 'material-theme-lighter',
dark: "material-theme", dark: 'material-theme',
}, },
themes: ["material-theme-lighter", "material-theme"], themes: ['material-theme-lighter', 'material-theme'],
}, },
}, },
routeRules: { routeRules: {
"/components": { redirect: "/components/button" }, '/components': { redirect: '/components/button' },
},
compatibilityDate: '2024-04-03',
typescript: {
includeWorkspace: true,
}, },
componentMeta: { componentMeta: {
exclude: [ exclude: [
"@nuxt/content", '@nuxt/content',
"@nuxtjs/color-mode", '@nuxtjs/color-mode',
"@nuxtjs/mdc", '@nuxtjs/mdc',
"nuxt/dist", 'nuxt/dist',
resolve("./components"), resolve('./components'),
], ],
metaFields: { metaFields: {
type: false, type: false,
@ -53,23 +59,19 @@ export default defineNuxtConfig({
exposed: false, exposed: false,
}, },
}, },
compatibilityDate: "2024-04-03",
typescript: {
includeWorkspace: true,
},
rayui: { rayui: {
globalComponents: true, globalComponents: true,
safeColors: [excludeColors(colors)], safeColors: [...excludeColors(colors)],
}, },
tailwindcss: { tailwindcss: {
config: { config: {
theme: { theme: {
extend: { extend: {
fontFamily: { fontFamily: {
sans: ["Rubik", '"Noto Sans SC"', ...defaultTheme.fontFamily.sans], sans: ['Rubik', '"Noto Sans SC"', ...defaultTheme.fontFamily.sans],
}, },
}, },
}, },
}, },
}, },
}); })