mirror of
https://github.com/HoshinoSuzumi/rayine-ui.git
synced 2025-05-08 04:52:29 +08:00
Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
d24140f673 | |||
e20572648d | |||
acee77ae7b | |||
236e08ad6b | |||
40ccfa0975 | |||
ec054a98fd | |||
55e9b5c09a | |||
1ed637cece | |||
8fefe70937 | |||
a8e47c6bff | |||
b30a52cfa0 | |||
c3147c2fd9 | |||
ddff1ca9c0 | |||
83f8593391 | |||
70d1af3d2f | |||
049739db91 | |||
17212d7982 | |||
69139c76b3 | |||
8123f5918a | |||
4c1df313ce | |||
2996866b9a | |||
1720987c4d | |||
fcae8047d3 | |||
28975d633c | |||
5e0e6bd1a6 | |||
817611c731 | |||
11018ba713 | |||
f52c45069f | |||
00a7c05aec | |||
77cc38e552 | |||
10b2c128d0 | |||
2729812a3c | |||
177c4cfd38 | |||
1f1647c4bc | |||
ab67a97ac9 | |||
f4d1d36f5b | |||
3061d73bc5 | |||
247f0c13af | |||
34c6641643 | |||
4ae71dd0e7 | |||
80c94ac457 | |||
e9b9b070f7 | |||
abd99b2e6d | |||
565a4b5e4f |
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: 'Bug: '
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: 'Feat: '
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
24
CHANGELOG.md
24
CHANGELOG.md
@ -1,5 +1,29 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.3.9](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.8...v1.3.9) (2024-11-27)
|
||||||
|
|
||||||
|
## [1.3.8](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.7...v1.3.8) (2024-11-27)
|
||||||
|
|
||||||
|
## [1.3.7](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.6...v1.3.7) (2024-11-26)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **button:** add `icon` and `loadingIcon` prop support ([2996866](https://github.com/HoshinoSuzumi/rayine-ui/commit/2996866b9adfed79fa64afa8ccd7c1fbfa88d059))
|
||||||
|
|
||||||
|
## [1.3.6](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.5...v1.3.6) (2024-11-26)
|
||||||
|
|
||||||
|
## [1.3.5](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.4...v1.3.5) (2024-11-26)
|
||||||
|
|
||||||
|
## [1.3.4](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.3...v1.3.4) (2024-11-25)
|
||||||
|
|
||||||
|
## [1.3.3](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.3-beta.1...v1.3.3) (2024-11-24)
|
||||||
|
|
||||||
|
## [1.3.3-beta.1](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.2...v1.3.3-beta.1) (2024-11-24)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **input:** update InputType in input.d.ts ([e9b9b07](https://github.com/HoshinoSuzumi/rayine-ui/commit/e9b9b070f75bd4b1c401801986e3208bf5b6aa0c))
|
||||||
|
|
||||||
## [1.3.2](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.1...v1.3.2) (2024-11-23)
|
## [1.3.2](https://github.com/HoshinoSuzumi/rayine-ui/compare/v1.3.1...v1.3.2) (2024-11-23)
|
||||||
|
|
||||||
## v1.3.1
|
## v1.3.1
|
||||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Timothy Yin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -1,5 +1,7 @@
|
|||||||
# Rayine UI
|
# Rayine UI
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
[![npm version][npm-version-src]][npm-version-href]
|
[![npm version][npm-version-src]][npm-version-href]
|
||||||
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
||||||
[![License][license-src]][license-href]
|
[![License][license-src]][license-href]
|
||||||
|
5
build.config.ts
Normal file
5
build.config.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { defineBuildConfig } from 'unbuild'
|
||||||
|
|
||||||
|
export default defineBuildConfig({
|
||||||
|
externals: ['#ray-colors'],
|
||||||
|
})
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg
|
<svg
|
||||||
class="w-9 h-9"
|
class="w-8 h-8"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
viewBox="0 0 970 1008"
|
viewBox="0 0 970 1008"
|
||||||
|
@ -11,7 +11,7 @@ const runtimeConfig = useRuntimeConfig().public
|
|||||||
:class="[route.path !== '/' ? 'border-b-neutral-200 dark:border-b-neutral-700' : 'border-b-transparent dark:border-b-transparent']"
|
:class="[route.path !== '/' ? 'border-b-neutral-200 dark:border-b-neutral-700' : 'border-b-transparent dark:border-b-transparent']"
|
||||||
>
|
>
|
||||||
<NuxtLink to="/" class="flex items-center gap-2 text-neutral-900 dark:text-neutral-100 group">
|
<NuxtLink to="/" class="flex items-center gap-2 text-neutral-900 dark:text-neutral-100 group">
|
||||||
<Logo />
|
<Logo class="-mt-0.5" />
|
||||||
<h1 class="flex flex-col">
|
<h1 class="flex flex-col">
|
||||||
<span class="block font-medium text-xl leading-none">
|
<span class="block font-medium text-xl leading-none">
|
||||||
RayineUI
|
RayineUI
|
||||||
@ -23,11 +23,9 @@ const runtimeConfig = useRuntimeConfig().public
|
|||||||
</h1>
|
</h1>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<NuxtLink
|
<RayButton to="https://github.com/HoshinoSuzumi/rayine-ui" target="_blank" icon="tabler:brand-github" variant="ghost">
|
||||||
to="https://github.com/HoshinoSuzumi/rayine-ui"
|
GitHub
|
||||||
target="_blank"
|
</RayButton>
|
||||||
class="text-neutral-400 dark:text-neutral-500"
|
|
||||||
>GitHub</NuxtLink>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { camelCase, upperFirst } from 'scule'
|
import { camelCase, upperFirst } from 'scule'
|
||||||
import json5 from 'json5'
|
import json5 from 'json5'
|
||||||
import * as config from '#rayui/ui.config'
|
import * as config from '#rayui/themes'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
@ -1,20 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { camelCase, kebabCase, upperFirst } from 'scule'
|
import { camelCase, kebabCase, upperFirst } from 'scule'
|
||||||
import FileTypeVue from '../icon/VscodeIconsFileTypeVue.vue'
|
|
||||||
import FileTypeTypescript from '../icon/VscodeIconsFileTypeTypescriptOfficial.vue'
|
|
||||||
import FileTypeJavascript from '../icon/VscodeIconsFileTypeJsOfficial.vue'
|
|
||||||
import TablerTerminal from '../icon/TablerTerminal.vue'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const appConfig = useAppConfig()
|
const appConfig = useAppConfig()
|
||||||
|
const { $prettier } = useNuxtApp()
|
||||||
const IconComponents = {
|
|
||||||
'vue': FileTypeVue,
|
|
||||||
'vue-html': FileTypeVue,
|
|
||||||
'sh': TablerTerminal,
|
|
||||||
'ts': FileTypeTypescript,
|
|
||||||
'js': FileTypeJavascript,
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
slug: {
|
slug: {
|
||||||
@ -25,25 +14,38 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
slots: {
|
privateProps: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
options: {
|
|
||||||
type: Array as PropType<{ name: string, values: string[], restriction: 'expected' | 'included' | 'excluded' | 'only' }[]>,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
excludedProps: {
|
excludedProps: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
slots: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array as PropType<{ name: string, values: string[], restriction: 'expected' | 'included' | 'excluded' | 'only' }[]>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
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 privateProps = reactive({ ...props.privateProps })
|
||||||
const componentProps = reactive({ ...props.props })
|
const componentProps = reactive({ ...props.props })
|
||||||
|
|
||||||
|
const componentFullProps = computed(() => ({ ...componentProps, ...privateProps }))
|
||||||
|
const componentVModel = computed({
|
||||||
|
get: () => privateProps.modelValue,
|
||||||
|
set: (value) => {
|
||||||
|
privateProps.modelValue = value
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
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 optionItem = props?.options?.find(item => item?.name === key) || null
|
const optionItem = props?.options?.find(item => item?.name === key) || null
|
||||||
@ -99,7 +101,7 @@ const customizableProps = computed(() => Object.keys(componentProps).map((k) =>
|
|||||||
return {
|
return {
|
||||||
name: k,
|
name: k,
|
||||||
type: prop?.type || 'string',
|
type: prop?.type || 'string',
|
||||||
label: camelCase(k),
|
label: k === 'modelValue' ? 'value' : camelCase(k),
|
||||||
options,
|
options,
|
||||||
}
|
}
|
||||||
}).filter(prop => prop !== null))
|
}).filter(prop => prop !== null))
|
||||||
@ -109,18 +111,47 @@ const code = computed(() => {
|
|||||||
<template>
|
<template>
|
||||||
<${componentName}`
|
<${componentName}`
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(componentProps)) {
|
for (const [k, v] of Object.entries(componentFullProps.value)) {
|
||||||
code += ` ${typeof v === 'boolean' || typeof v === 'number' || typeof v === 'object' ? ':' : ''}${kebabCase(k)}="${typeof v === 'object' ? renderObject(v) : v}"`
|
if (v === 'undefined' || v === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
code += ` ${(typeof v === 'boolean' && (k === 'modelValue' || v !== true)) || typeof v === 'number' || typeof v === 'object' ? ':' : ''}${k === 'modelValue' ? 'model-value' : kebabCase(k)}${k !== 'modelValue' && typeof v === 'boolean' && !!v ? '' : `="${typeof v === 'object' ? renderObject(v) : v}"`}`
|
||||||
}
|
}
|
||||||
|
|
||||||
code += `/>\n</template>
|
if (props.slots) {
|
||||||
|
code += `>
|
||||||
|
${Object.entries(props.slots).map(([key, value]) => {
|
||||||
|
return key === 'default'
|
||||||
|
? value
|
||||||
|
: `<template #${key}>
|
||||||
|
${value}
|
||||||
|
</template>`
|
||||||
|
}).join('\n ')}
|
||||||
|
</${componentName}>`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
code += ' />'
|
||||||
|
}
|
||||||
|
|
||||||
|
code += `\n</template>
|
||||||
\`\`\`
|
\`\`\`
|
||||||
`
|
`
|
||||||
return code
|
return code
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: codeRender } = await useAsyncData(`${componentName}-code-renderer-${JSON.stringify({ props: componentProps, slots: props.slots, code: code.value })}`, async () => {
|
const { data: codeRender } = await useAsyncData(`${componentName}-code-renderer-${JSON.stringify({ props: componentProps, slots: props.slots, code: code.value })}`, async () => {
|
||||||
return parseMarkdown(code.value, {})
|
let fortmattedCode = ''
|
||||||
|
try {
|
||||||
|
fortmattedCode = await $prettier.format(code.value, {
|
||||||
|
semi: false,
|
||||||
|
singleQuote: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
fortmattedCode = code.value
|
||||||
|
}
|
||||||
|
return parseMarkdown(fortmattedCode)
|
||||||
}, {
|
}, {
|
||||||
watch: [code],
|
watch: [code],
|
||||||
})
|
})
|
||||||
@ -128,9 +159,12 @@ const { data: codeRender } = await useAsyncData(`${componentName}-code-renderer-
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="border border-neutral-200 dark:border-neutral-700 rounded-lg not-prose my-2 overflow-hidden">
|
<div class="border border-neutral-200 dark:border-neutral-700 rounded-lg not-prose my-2 overflow-hidden">
|
||||||
<div :class="['p-4 overflow-auto', !!codeRender ? 'border-b border-neutral-200 dark:border-neutral-700' : '']">
|
<div :class="['p-4 overflow-auto flex', !!codeRender ? 'border-b border-neutral-200 dark:border-neutral-700' : '']">
|
||||||
<component :is="componentName" v-bind="componentProps">
|
<component :is="componentName" v-model="componentVModel" v-bind="componentFullProps">
|
||||||
<slot />
|
<ContentSlot v-if="$slots.default" :use="$slots.default" />
|
||||||
|
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
|
||||||
|
<ContentSlot :name="slot" unwrap="p" />
|
||||||
|
</template>
|
||||||
</component>
|
</component>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -153,17 +187,22 @@ const { data: codeRender } = await useAsyncData(`${componentName}-code-renderer-
|
|||||||
{{ option }}
|
{{ option }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<input
|
<RayInput
|
||||||
v-else
|
v-else
|
||||||
:id="`${prop.name}-prop`"
|
:id="`${prop.name}-prop`"
|
||||||
v-model="componentProps[prop.name]"
|
:model-value="componentProps[prop.name]"
|
||||||
type="text"
|
:type="prop.type.includes('number') ? 'number' : 'text'"
|
||||||
placeholder="type something..."
|
variant="plain"
|
||||||
>
|
:padded="false"
|
||||||
|
:ui="{ rounded: 'rounded-none' }"
|
||||||
|
:placeholder="prop.type"
|
||||||
|
autocomplete="off"
|
||||||
|
@update:model-value="val => componentProps[prop.name] = prop.type === 'number' ? Number(val) : val"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ContentRenderer v-if="codeRender" :value="codeRender" class="overflow-auto [&_.pre]:rounded-none [&_.pre]:border-none" />
|
<ContentRenderer v-if="codeRender" :value="codeRender" class="[&_.pre]:rounded-none [&_.pre]:border-none" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
50
docs/components/content/ComponentProps.vue
Normal file
50
docs/components/content/ComponentProps.vue
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { ComponentMeta } from 'vue-component-meta'
|
||||||
|
import { camelCase, upperFirst } from 'scule'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
slug: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
||||||
|
const componentCamelName = camelCase(slug)
|
||||||
|
const componentName = `Ray${upperFirst(componentCamelName)}`
|
||||||
|
|
||||||
|
const meta = await fetchComponentMeta(componentName)
|
||||||
|
const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => meta?.meta?.props || [])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProseTable>
|
||||||
|
<ProseThead>
|
||||||
|
<ProseTr>
|
||||||
|
<ProseTh>Prop</ProseTh>
|
||||||
|
<ProseTh>Default</ProseTh>
|
||||||
|
<ProseTh>Type</ProseTh>
|
||||||
|
</ProseTr>
|
||||||
|
</ProseThead>
|
||||||
|
<ProseTbody>
|
||||||
|
<ProseTr v-for="prop in metaProps" :key="prop.name">
|
||||||
|
<ProseTd>
|
||||||
|
{{ prop.name }}
|
||||||
|
</ProseTd>
|
||||||
|
<ProseTd>
|
||||||
|
<ProseCodeInline v-if="prop.default">
|
||||||
|
{{ prop.default }}
|
||||||
|
</ProseCodeInline>
|
||||||
|
</ProseTd>
|
||||||
|
<ProseTd>
|
||||||
|
<ProseCodeInline v-if="prop.type">
|
||||||
|
{{ prop.type }}
|
||||||
|
</ProseCodeInline>
|
||||||
|
<MDC v-if="prop.description" :value="prop.description" class="text-gray-500 dark:text-gray-400" />
|
||||||
|
</ProseTd>
|
||||||
|
</ProseTr>
|
||||||
|
</ProseTbody>
|
||||||
|
</ProseTable>
|
||||||
|
</template>
|
26
docs/components/content/ComponentSlots.vue
Normal file
26
docs/components/content/ComponentSlots.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { camelCase, upperFirst } from 'scule'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
slug: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
||||||
|
const componentCamelName = camelCase(slug)
|
||||||
|
const componentName = `Ray${upperFirst(componentCamelName)}`
|
||||||
|
|
||||||
|
const meta = await fetchComponentMeta(componentName)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col not-prose font-mono divide-y divide-gray-100 dark:divide-gray-800">
|
||||||
|
<div v-for="(slot, k) in meta?.meta?.slots" :key="k" class="py-2">
|
||||||
|
<pre>{{ slot }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -1,12 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FileTypeVue from '../../icon/VscodeIconsFileTypeVue.vue'
|
|
||||||
import FileTypeTypescript from '../../icon/VscodeIconsFileTypeTypescriptOfficial.vue'
|
|
||||||
import FileTypeJavascript from '../../icon/VscodeIconsFileTypeJsOfficial.vue'
|
|
||||||
import TablerTerminal from '../../icon/TablerTerminal.vue'
|
|
||||||
import TablerFile from '~/components/icon/TablerFile.vue'
|
|
||||||
import VscodeIconsFileTypeJson from '~/components/icon/VscodeIconsFileTypeJson.vue'
|
|
||||||
import VscodeIconsFileTypeNuxt from '~/components/icon/VscodeIconsFileTypeNuxt.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
code: {
|
code: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -38,29 +30,39 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapIconLanguage = {
|
const iconNameLangMapping: Record<string, string> = {
|
||||||
'default': TablerFile,
|
'default': 'tabler:file',
|
||||||
'vue': FileTypeVue,
|
'vue': 'vscode-icons:file-type-vue',
|
||||||
'vue-html': FileTypeVue,
|
'vue-html': 'vscode-icons:file-type-vue',
|
||||||
'bash': TablerTerminal,
|
'bash': 'tabler:terminal',
|
||||||
'sh': TablerTerminal,
|
'sh': 'tabler:terminal',
|
||||||
'ts': FileTypeTypescript,
|
'ts': 'vscode-icons:file-type-typescript-official',
|
||||||
'js': FileTypeJavascript,
|
'js': 'vscode-icons:file-type-js-official',
|
||||||
'json': VscodeIconsFileTypeJson,
|
'json': 'vscode-icons:file-type-json',
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapIconFilename = {
|
const iconNameFilenameMapping: Record<string, string> = {
|
||||||
'nuxt.config.ts': VscodeIconsFileTypeNuxt,
|
'nuxt.config.ts': 'vscode-icons:file-type-nuxt',
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveIcon = computed(() => {
|
const resolvedIconName = computed(() => {
|
||||||
if (props.filename) {
|
if (!props.language) {
|
||||||
if (props.filename.endsWith('.vue')) return FileTypeVue
|
return iconNameLangMapping['default']
|
||||||
|
|
||||||
const icon = mapIconFilename[props.filename as keyof typeof mapIconFilename]
|
|
||||||
if (icon) return icon
|
|
||||||
}
|
}
|
||||||
return mapIconLanguage[props.language as keyof typeof mapIconLanguage]
|
|
||||||
|
if (props.filename.endsWith('.vue')) {
|
||||||
|
return iconNameLangMapping['vue']
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iconNameFilenameMapping[props.filename]) {
|
||||||
|
return iconNameFilenameMapping[props.filename]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iconNameLangMapping[props.language]) {
|
||||||
|
return iconNameLangMapping[props.language]
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconNameLangMapping['default']
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ const resolveIcon = computed(() => {
|
|||||||
<div data-prose-pre class="pre rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700">
|
<div data-prose-pre class="pre rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700">
|
||||||
<div v-if="filename" class="p-4 py-2 border-b border-neutral-200 dark:border-neutral-700">
|
<div v-if="filename" class="p-4 py-2 border-b border-neutral-200 dark:border-neutral-700">
|
||||||
<span class="flex items-center gap-1">
|
<span class="flex items-center gap-1">
|
||||||
<component :is="resolveIcon" v-if="language" class="inline" />
|
<RayIcon v-if="resolvedIconName" :name="resolvedIconName" class="inline" />
|
||||||
<span class="text-sm text-neutral-500 dark:text-neutral-400">{{ filename }}</span>
|
<span class="text-sm text-neutral-500 dark:text-neutral-400">{{ filename }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M14 3v4a1 1 0 0 0 1 1h4" /><path d="M17 21H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2" /></g></svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'TablerFile',
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,16 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="m5 7l5 5l-5 5m7 2h7"
|
|
||||||
/></svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'TablerTerminal',
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"><path fill="#f5de19" d="M2 2h28v28H2z" /><path d="M20.809 23.875a2.87 2.87 0 0 0 2.6 1.6c1.09 0 1.787-.545 1.787-1.3c0-.9-.716-1.222-1.916-1.747l-.658-.282c-1.9-.809-3.16-1.822-3.16-3.964c0-1.973 1.5-3.476 3.853-3.476a3.89 3.89 0 0 1 3.742 2.107L25 18.128A1.79 1.79 0 0 0 23.311 17a1.145 1.145 0 0 0-1.259 1.128c0 .789.489 1.109 1.618 1.6l.658.282c2.236.959 3.5 1.936 3.5 4.133c0 2.369-1.861 3.667-4.36 3.667a5.06 5.06 0 0 1-4.795-2.691Zm-9.295.228c.413.733.789 1.353 1.693 1.353c.864 0 1.41-.338 1.41-1.653v-8.947h2.631v8.982c0 2.724-1.6 3.964-3.929 3.964a4.085 4.085 0 0 1-3.947-2.4Z" /></svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'VscodeIconsFileTypeJsOfficial',
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"><path fill="#f5de19" d="M4.014 14.976a2.5 2.5 0 0 0 1.567-.518a2.38 2.38 0 0 0 .805-1.358a15.3 15.3 0 0 0 .214-2.944q.012-2.085.075-2.747a5.2 5.2 0 0 1 .418-1.686a3 3 0 0 1 .755-1.018A3.05 3.05 0 0 1 9 4.125A6.8 6.8 0 0 1 10.544 4h.7v1.96h-.387a2.34 2.34 0 0 0-1.723.468a3.4 3.4 0 0 0-.425 2.092a36 36 0 0 1-.137 4.133a4.7 4.7 0 0 1-.768 2.06A4.6 4.6 0 0 1 6.1 16a3.8 3.8 0 0 1 1.992 1.754a8.9 8.9 0 0 1 .618 3.865q0 2.435.05 2.9a1.76 1.76 0 0 0 .504 1.181a2.64 2.64 0 0 0 1.592.337h.387V28h-.7a5.7 5.7 0 0 1-1.773-.2a2.97 2.97 0 0 1-1.324-.93a3.35 3.35 0 0 1-.681-1.63a24 24 0 0 1-.165-3.234a16.5 16.5 0 0 0-.214-3.106a2.4 2.4 0 0 0-.805-1.361a2.5 2.5 0 0 0-1.567-.524Zm23.972 2.035a2.5 2.5 0 0 0-1.567.524a2.4 2.4 0 0 0-.805 1.361a16.5 16.5 0 0 0-.212 3.109a24 24 0 0 1-.169 3.234a3.35 3.35 0 0 1-.681 1.63a2.97 2.97 0 0 1-1.324.93a5.7 5.7 0 0 1-1.773.2h-.7V26.04h.387a2.64 2.64 0 0 0 1.592-.337a1.76 1.76 0 0 0 .506-1.186q.05-.462.05-2.9a8.9 8.9 0 0 1 .618-3.865A3.8 3.8 0 0 1 25.9 16a4.6 4.6 0 0 1-1.7-1.286a4.7 4.7 0 0 1-.768-2.06a36 36 0 0 1-.137-4.133a3.4 3.4 0 0 0-.425-2.092a2.34 2.34 0 0 0-1.723-.468h-.387V4h.7a6.8 6.8 0 0 1 1.54.125a3.05 3.05 0 0 1 1.149.581a3 3 0 0 1 .755 1.018a5.2 5.2 0 0 1 .418 1.686q.062.662.075 2.747a15.3 15.3 0 0 0 .212 2.947a2.38 2.38 0 0 0 .805 1.355a2.5 2.5 0 0 0 1.567.518Z" /></svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'VscodeIconsFileTypeJson',
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"><path fill="#00DC82" d="M17.708 25h10.409c.33 0 .655-.088.942-.254a1.9 1.9 0 0 0 .689-.696a1.91 1.91 0 0 0 0-1.9L22.756 9.936a1.87 1.87 0 0 0-3.261 0l-1.788 3.125l-3.494-6.111a1.871 1.871 0 0 0-3.262 0l-8.7 15.2a1.91 1.91 0 0 0 .69 2.595c.286.167.61.255.941.255h6.534c2.589 0 4.498-1.147 5.811-3.385l3.19-5.572l1.708-2.982l5.127 8.957h-6.835zm-7.398-2.985l-4.56-.001l6.836-11.942l3.41 5.97l-2.283 3.992c-.873 1.452-1.864 1.981-3.403 1.981" /></svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'VscodeIconsFileTypeNuxt',
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,16 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"><rect
|
|
||||||
width="28"
|
|
||||||
height="28"
|
|
||||||
x="2"
|
|
||||||
y="2"
|
|
||||||
fill="#3178c6"
|
|
||||||
rx="1.312"
|
|
||||||
/><path fill="#fff" fillRule="evenodd" d="M18.245 23.759v3.068a6.5 6.5 0 0 0 1.764.575a11.6 11.6 0 0 0 2.146.192a10 10 0 0 0 2.088-.211a5.1 5.1 0 0 0 1.735-.7a3.54 3.54 0 0 0 1.181-1.266a4.47 4.47 0 0 0 .186-3.394a3.4 3.4 0 0 0-.717-1.117a5.2 5.2 0 0 0-1.123-.877a12 12 0 0 0-1.477-.734q-.6-.249-1.08-.484a5.5 5.5 0 0 1-.813-.479a2.1 2.1 0 0 1-.516-.518a1.1 1.1 0 0 1-.181-.618a1.04 1.04 0 0 1 .162-.571a1.4 1.4 0 0 1 .459-.436a2.4 2.4 0 0 1 .726-.283a4.2 4.2 0 0 1 .956-.1a6 6 0 0 1 .808.058a6 6 0 0 1 .856.177a6 6 0 0 1 .836.3a4.7 4.7 0 0 1 .751.422V13.9a7.5 7.5 0 0 0-1.525-.4a12.4 12.4 0 0 0-1.9-.129a8.8 8.8 0 0 0-2.064.235a5.2 5.2 0 0 0-1.716.733a3.66 3.66 0 0 0-1.171 1.271a3.73 3.73 0 0 0-.431 1.845a3.6 3.6 0 0 0 .789 2.34a6 6 0 0 0 2.395 1.639q.63.26 1.175.509a6.5 6.5 0 0 1 .942.517a2.5 2.5 0 0 1 .626.585a1.2 1.2 0 0 1 .23.719a1.1 1.1 0 0 1-.144.552a1.3 1.3 0 0 1-.435.441a2.4 2.4 0 0 1-.726.292a4.4 4.4 0 0 1-1.018.105a5.8 5.8 0 0 1-1.969-.35a5.9 5.9 0 0 1-1.805-1.045m-5.154-7.638h4v-2.527H5.938v2.527H9.92v11.254h3.171Z" /></svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'VscodeIconsFileTypeTypescriptOfficial',
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"><path fill="#41b883" d="M24.4 3.925H30l-14 24.15L2 3.925h10.71l3.29 5.6l3.22-5.6Z" /><path fill="#41b883" d="m2 3.925l14 24.15l14-24.15h-5.6L16 18.415L7.53 3.925Z" /><path fill="#35495e" d="M7.53 3.925L16 18.485l8.4-14.56h-5.18L16 9.525l-3.29-5.6Z" /></svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'VscodeIconsFileTypeVue',
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -12,3 +12,4 @@ This project aims to facilitate sharing a component library across multiple proj
|
|||||||
- Fully customizable components
|
- Fully customizable components
|
||||||
- TailwindCSS inside
|
- TailwindCSS inside
|
||||||
- Full TypeScript support
|
- Full TypeScript support
|
||||||
|
- 200,000+ icons from [Iconify](https://iconify.design/)
|
||||||
|
@ -91,6 +91,18 @@ props:
|
|||||||
Button
|
Button
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Icon
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
icon: tabler:adjustments
|
||||||
|
size: sm
|
||||||
|
loading: false
|
||||||
|
---
|
||||||
|
Settings
|
||||||
|
::
|
||||||
|
|
||||||
### Loading
|
### Loading
|
||||||
|
|
||||||
::ComponentPreview
|
::ComponentPreview
|
||||||
@ -102,7 +114,14 @@ props:
|
|||||||
Button
|
Button
|
||||||
::
|
::
|
||||||
|
|
||||||
## Config
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
::ComponentProps
|
||||||
|
::
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
::ComponentDefaults
|
::ComponentDefaults
|
||||||
::
|
::
|
||||||
|
24
docs/content/2.components/icon.md
Normal file
24
docs/content/2.components/icon.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
description: Add icons to your app. Based on Iconify
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This component is a wrapper based on the `@nuxt/icon` library, which is based on Iconify. So you can use any icon name available on [icones.js.org](https://icones.js.org/).
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
class: w-6 h-6
|
||||||
|
props:
|
||||||
|
name: tabler:brand-github
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## Collections
|
||||||
|
|
||||||
|
It's recommended to install the icon collection you want to use locally. You can do this by running:
|
||||||
|
|
||||||
|
```bash [Terminal]
|
||||||
|
pnpm i @iconify-icons/[collection_name]
|
||||||
|
```
|
155
docs/content/2.components/input.md
Normal file
155
docs/content/2.components/input.md
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
---
|
||||||
|
description: The input component is used to get user input
|
||||||
|
since: 1.3.2
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The basic usage.
|
||||||
|
|
||||||
|
:::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: "Type something..."
|
||||||
|
---
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Sizes
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: "Type something..."
|
||||||
|
props:
|
||||||
|
size: sm
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
|
||||||
|
The `color` prop affects the color of the border.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
color: primary
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Variants
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: "Search..."
|
||||||
|
props:
|
||||||
|
variant: outline
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Type
|
||||||
|
|
||||||
|
The `type` prop changes the type of the input. All the aviailable types can be found at [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: "Type anything..."
|
||||||
|
props:
|
||||||
|
type: text
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Placeholder
|
||||||
|
|
||||||
|
The `placeholder` prop sets the placeholder text. It is shown when the input is empty.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
placeholder: "Type anything..."
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Padded
|
||||||
|
|
||||||
|
Inputs can be with no padding.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: "Search..."
|
||||||
|
variant: plain
|
||||||
|
props:
|
||||||
|
padded: false
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Disabled
|
||||||
|
|
||||||
|
Inputs can be disabled.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: "Search..."
|
||||||
|
props:
|
||||||
|
disabled: true
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Model Modifiers
|
||||||
|
|
||||||
|
#### .trim
|
||||||
|
|
||||||
|
The `.trim` modifier trims the input value.
|
||||||
|
|
||||||
|
```vue [page]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const modal = ref<string>("");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RayInput v-model.trim="modal" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### .number
|
||||||
|
|
||||||
|
The `.number` modifier converts the input value to a number. Non-numeric values are ignored.
|
||||||
|
|
||||||
|
```vue [page]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const modal = ref<number>(0);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RayInput v-model.number="modal" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### .lazy
|
||||||
|
|
||||||
|
The `.lazy` modifier syncs the input value with the model only on `change` event.
|
||||||
|
|
||||||
|
```vue [page]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const modal = ref<string>("");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RayInput v-model.lazy="modal" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
::ComponentProps
|
||||||
|
::
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
|
::ComponentDefaults
|
||||||
|
::
|
57
docs/content/2.components/kbd.md
Normal file
57
docs/content/2.components/kbd.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
description: Display a keyboard keys such as shortcuts or hotkeys
|
||||||
|
since: 1.3.3
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use the default slot to display the keyboard key.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
K
|
||||||
|
::
|
||||||
|
|
||||||
|
The `label` prop also can be used to do so.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
label: K
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Sizes
|
||||||
|
|
||||||
|
The `size` prop changes the size of the `kbd`.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
size: sm
|
||||||
|
---
|
||||||
|
K
|
||||||
|
::
|
||||||
|
|
||||||
|
### Shadow
|
||||||
|
|
||||||
|
Add a shadow to the `kbd`.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
shadow: true
|
||||||
|
---
|
||||||
|
K
|
||||||
|
::
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
::ComponentProps
|
||||||
|
::
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
|
::ComponentDefaults
|
||||||
|
::
|
95
docs/content/2.components/mark.md
Normal file
95
docs/content/2.components/mark.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
description: Display a indicator with or without counts on any component
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use the default slot to add any component you want to display the indicator on.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
slots:
|
||||||
|
default: |
|
||||||
|
<RayButton icon="tabler:message" label="messages" color="invert" />
|
||||||
|
---
|
||||||
|
#default
|
||||||
|
:RayButton{icon="tabler:message" label="messages" color="invert"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Styles
|
||||||
|
|
||||||
|
You can change the color and size of the indicator by using the `color` and `size` props.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
color: amber
|
||||||
|
size: sm
|
||||||
|
slots:
|
||||||
|
default: |
|
||||||
|
<RayButton icon="tabler:message" label="messages" color="invert" />
|
||||||
|
---
|
||||||
|
#default
|
||||||
|
:RayButton{icon="tabler:message" label="messages" color="invert"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Position
|
||||||
|
|
||||||
|
Use the `position` prop to change the position of the indicator.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
position: top-right
|
||||||
|
slots:
|
||||||
|
default: |
|
||||||
|
<RayButton icon="tabler:message" label="messages" color="invert" />
|
||||||
|
---
|
||||||
|
#default
|
||||||
|
:RayButton{icon="tabler:message" label="messages" color="invert"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### Count
|
||||||
|
|
||||||
|
Add a count to the indicator by using the `value` prop.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
value: 5
|
||||||
|
slots:
|
||||||
|
default: |
|
||||||
|
<RayButton icon="tabler:message" label="messages" color="invert" />
|
||||||
|
---
|
||||||
|
#default
|
||||||
|
:RayButton{icon="tabler:message" label="messages" color="invert"}
|
||||||
|
::
|
||||||
|
|
||||||
|
#### Overflow
|
||||||
|
|
||||||
|
Set `max` prop to handle overflow situation.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
value: 110
|
||||||
|
max: 99
|
||||||
|
slots:
|
||||||
|
default: |
|
||||||
|
<RayButton icon="tabler:message" label="messages" color="invert" />
|
||||||
|
---
|
||||||
|
#default
|
||||||
|
:RayButton{icon="tabler:message" label="messages" color="invert"}
|
||||||
|
::
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
::ComponentProps
|
||||||
|
::
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
|
::ComponentDefaults
|
||||||
|
::
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: The message component is used to display a message to the user
|
description: The message component is used to display a message to the user
|
||||||
|
since: 1.2.0
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -47,6 +48,20 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### Icon
|
||||||
|
|
||||||
|
Or you can use the `icon` prop to change the icon of the message.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
content: Thanks for activating
|
||||||
|
props:
|
||||||
|
icon: tabler:circle-key
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
|
|
||||||
Use the `color` prop to change the color of the message.
|
Use the `color` prop to change the color of the message.
|
||||||
@ -59,7 +74,14 @@ props:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
## Config
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
::ComponentProps
|
||||||
|
::
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
::ComponentDefaults
|
::ComponentDefaults
|
||||||
::
|
::
|
||||||
|
189
docs/content/2.components/textarea.md
Normal file
189
docs/content/2.components/textarea.md
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
---
|
||||||
|
description: Create a textarea component
|
||||||
|
since: 1.3.5
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The basic usage.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Sizes
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
size: sm
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
color: primary
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Variants
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
variant: outline
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Placeholder
|
||||||
|
|
||||||
|
You can also set a placeholder.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
placeholder: "Description here..."
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Padded
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
variant: plain
|
||||||
|
props:
|
||||||
|
padded: false
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Rows
|
||||||
|
|
||||||
|
Set the number of rows of the textarea.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
rows: 4
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Resize
|
||||||
|
|
||||||
|
Enable the resize control.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
resize: true
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Auto Resize
|
||||||
|
|
||||||
|
The `autosize` prop enables the auto resizing of the textarea. The textarea will grow in height as the user types.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
autosize: true
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
The `maxrows` prop can be used to set the maximum number of rows the textarea can grow to.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
autosize: true
|
||||||
|
maxrows: 8
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Disabled
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
placeholder: Description
|
||||||
|
props:
|
||||||
|
disabled: true
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Model Modifiers
|
||||||
|
|
||||||
|
#### .trim
|
||||||
|
|
||||||
|
The `.trim` modifier trims the input value.
|
||||||
|
|
||||||
|
```vue [page]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const modal = ref<string>("");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RayTextarea v-model.trim="modal" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### .number
|
||||||
|
|
||||||
|
The `.number` modifier converts the input value to a number. Non-numeric values are ignored.
|
||||||
|
|
||||||
|
```vue [page]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const modal = ref<number>(0);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RayTextarea v-model.number="modal" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### .lazy
|
||||||
|
|
||||||
|
The `.lazy` modifier syncs the input value with the model only on `change` event.
|
||||||
|
|
||||||
|
```vue [page]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const modal = ref<string>("");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RayTextarea v-model.lazy="modal" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
::ComponentProps
|
||||||
|
::
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
|
::ComponentDefaults
|
||||||
|
::
|
76
docs/content/2.components/toggle.md
Normal file
76
docs/content/2.components/toggle.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
description: Get a dynamic switch component
|
||||||
|
since: 1.3.4
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use the `v-model` directive to make it reactive.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
v-model: checked
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
|
||||||
|
The `color` prop affects the background color of the toggle.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
modelValue: true
|
||||||
|
props:
|
||||||
|
color: primary
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Sizes
|
||||||
|
|
||||||
|
The default size of the toggle is `md`.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
size: md
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Rounded
|
||||||
|
|
||||||
|
You can make the toggle rounded by setting the `rounded` prop to `true`.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
rounded: true
|
||||||
|
size: md
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Disabled
|
||||||
|
|
||||||
|
Disable it.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
modelValue: true
|
||||||
|
props:
|
||||||
|
disabled: true
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
::ComponentProps
|
||||||
|
::
|
||||||
|
|
||||||
|
### Theme
|
||||||
|
|
||||||
|
::ComponentDefaults
|
||||||
|
::
|
@ -50,6 +50,7 @@ export default defineNuxtConfig({
|
|||||||
componentMeta: {
|
componentMeta: {
|
||||||
exclude: [
|
exclude: [
|
||||||
'@nuxt/content',
|
'@nuxt/content',
|
||||||
|
'@nuxt/icon',
|
||||||
'@nuxtjs/color-mode',
|
'@nuxtjs/color-mode',
|
||||||
'@nuxtjs/mdc',
|
'@nuxtjs/mdc',
|
||||||
'nuxt/dist',
|
'nuxt/dist',
|
||||||
@ -59,10 +60,15 @@ export default defineNuxtConfig({
|
|||||||
type: false,
|
type: false,
|
||||||
props: true,
|
props: true,
|
||||||
slots: true,
|
slots: true,
|
||||||
events: false,
|
events: true,
|
||||||
exposed: false,
|
exposed: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
clientBundle: {
|
||||||
|
scan: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
rayui: {
|
rayui: {
|
||||||
globalComponents: true,
|
globalComponents: true,
|
||||||
safeColors: [...excludeColors(colors)],
|
safeColors: [...excludeColors(colors)],
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rayine-ui-docs",
|
"name": "rayine-ui-docs",
|
||||||
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
@ -9,13 +10,15 @@
|
|||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@iconify-json/vscode-icons": "^1.2.2",
|
||||||
"@nuxt/content": "^2.13.4",
|
"@nuxt/content": "^2.13.4",
|
||||||
"@nuxtjs/color-mode": "^3.5.2",
|
"@nuxtjs/color-mode": "^3.5.2",
|
||||||
"@nuxtjs/mdc": "^0.9.2",
|
"@nuxtjs/mdc": "^0.9.2",
|
||||||
"nuxt": "^3.14.159",
|
"nuxt": "^3.14.159",
|
||||||
"nuxt-component-meta": "^0.9.0",
|
"nuxt-component-meta": "^0.9.0",
|
||||||
"nuxt-shiki": "^0.3.0",
|
"nuxt-shiki": "^0.3.0",
|
||||||
"rayine-ui": "workspace:rayine-ui",
|
"prettier": "^3.3.3",
|
||||||
|
"rayine-ui": "latest",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { withoutTrailingSlash } from 'ufo'
|
import { withoutTrailingSlash } from 'ufo'
|
||||||
|
import { standard } from '#rayui/themes'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@ -40,10 +41,10 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
<li v-for="child in link.children" :key="child._path">
|
<li v-for="child in link.children" :key="child._path">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="child._path"
|
:to="child._path"
|
||||||
class="text-sm text-neutral-500 dark:text-neutral-400"
|
class="text-sm text-neutral-500 dark:text-neutral-400 flex items-center gap-1"
|
||||||
active-class="text-primary dark:text-primary font-medium"
|
active-class="text-primary dark:text-primary font-medium"
|
||||||
>
|
>
|
||||||
{{ child.title }}
|
<span>{{ child.title }}</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -55,9 +56,19 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
|
|
||||||
<div class="col-span-12" :class="[hasToc ? 'md:col-span-8' : 'md:col-span-10']">
|
<div class="col-span-12" :class="[hasToc ? 'md:col-span-8' : 'md:col-span-10']">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-3xl text-primary font-medium">
|
<div class="flex justify-between items-center">
|
||||||
{{ page?.title || 'untitled' }}
|
<h1 class="text-3xl text-primary font-medium">
|
||||||
</h1>
|
{{ page?.title || 'untitled' }}
|
||||||
|
</h1>
|
||||||
|
<div
|
||||||
|
v-if="page?.since"
|
||||||
|
class="ring-1 ring-inset ring-primary-200 dark:ring-primary-900 text-primary-500 dark:text-primary-400 rounded-md bg-primary-50 dark:bg-primary-900 font-medium flex items-center gap-1"
|
||||||
|
:class="[standard.padding['sm'], standard.size['2xs']]"
|
||||||
|
>
|
||||||
|
<RayIcon name="tabler:git-merge" class="text-sm -mt-0.5" />
|
||||||
|
v{{ page.since }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<p v-if="page?.description" class="text-lg text-neutral-500 dark:text-neutral-400 mt-2">
|
<p v-if="page?.description" class="text-lg text-neutral-500 dark:text-neutral-400 mt-2">
|
||||||
{{ page.description }}
|
{{ page.description }}
|
||||||
</p>
|
</p>
|
||||||
@ -126,7 +137,7 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.surround-btn {
|
.surround-btn {
|
||||||
@apply font-medium;
|
@apply font-medium;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
@apply bg-neutral-100 dark:bg-neutral-800 rounded-lg px-8 py-6 w-full h-full flex flex-col gap-0 border border-transparent;
|
@apply bg-neutral-100 dark:bg-neutral-800 rounded-lg px-8 py-6 w-full h-full flex flex-col gap-0 border border-transparent;
|
||||||
|
73
docs/plugins/prettier.ts
Normal file
73
docs/plugins/prettier.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// ref: https://github.com/nuxt/ui/blob/f3632ddee511f0fccb24d4fc37403421e84ffdae/docs/plugins/prettier.ts
|
||||||
|
import type { Options } from 'prettier'
|
||||||
|
import { defu } from 'defu'
|
||||||
|
import PrettierWorker from '@/workers/prettier.js?worker&inline'
|
||||||
|
|
||||||
|
export interface SimplePrettier {
|
||||||
|
format: (source: string, options?: Options) => Promise<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPrettierWorkerApi(worker: Worker): SimplePrettier {
|
||||||
|
let counter = 0
|
||||||
|
const handlers: any = {}
|
||||||
|
|
||||||
|
worker.addEventListener('message', (event) => {
|
||||||
|
const { uid, message, error } = event.data
|
||||||
|
|
||||||
|
if (!handlers[uid]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const [resolve, reject] = handlers[uid]
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
|
delete handlers[uid]
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolve(message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function postMessage<T>(message: any) {
|
||||||
|
const uid = ++counter
|
||||||
|
return new Promise<T>((resolve, reject) => {
|
||||||
|
handlers[uid] = [resolve, reject]
|
||||||
|
worker.postMessage({ uid, message })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
format(source: string, options?: Options) {
|
||||||
|
return postMessage({ type: 'format', source, options })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(async () => {
|
||||||
|
let prettier: SimplePrettier
|
||||||
|
if (import.meta.server) {
|
||||||
|
const prettierModule = await import('prettier')
|
||||||
|
prettier = {
|
||||||
|
format(source, options = {}) {
|
||||||
|
return prettierModule.format(
|
||||||
|
source,
|
||||||
|
defu(options, {
|
||||||
|
parser: 'markdown',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const worker = new PrettierWorker()
|
||||||
|
prettier = createPrettierWorkerApi(worker)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
provide: {
|
||||||
|
prettier,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
6378
docs/pnpm-lock.yaml
generated
6378
docs/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
BIN
docs/public/rayine.png
Normal file
BIN
docs/public/rayine.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
33
docs/public/rayine_no_shadow.svg
Normal file
33
docs/public/rayine_no_shadow.svg
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="rayine" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #3f3f46;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-1, .cls-2, .cls-3 {
|
||||||
|
stroke-width: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #71717a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-3 {
|
||||||
|
fill: #a5b4fc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="r_leg">
|
||||||
|
<path class="cls-1" d="m949.3,851.91v121.45c0,15.27-12.38,27.64-27.64,27.64h-122.51c-7.33,0-14.36-2.91-19.55-8.1l-384.22-384.22,262.61-3.69c18.78,0,28.64-6.04,46.42-9.42l236.79,236.79c5.18,5.18,8.1,12.22,8.1,19.55Z"/>
|
||||||
|
</g>
|
||||||
|
<path id="r_head" class="cls-2" d="m949.3,316.84c0,142.4-102,260.99-236.93,286.69-17.79,3.38-36.14,5.16-54.91,5.16h-262.07v-208.38h207.85c20.44,0,39.18-7.36,53.69-19.57,18.2-15.3,29.77-38.24,29.77-63.89s-11.57-48.59-29.77-63.89c-14.51-12.22-33.25-19.57-53.69-19.57-.18,0-.35,0-.53.01h0s-207.32-.01-207.32-.01V25h262.07c161.18,0,291.84,130.66,291.84,291.84Z"/>
|
||||||
|
<g id="cube_3">
|
||||||
|
<rect class="cls-1" x="76.7" y="691.61" width="249.85" height="309.39" rx="26" ry="26"/>
|
||||||
|
</g>
|
||||||
|
<rect id="cube_2" class="cls-3" x="76.7" y="400.3" width="249.85" height="208.38" rx="26" ry="26"/>
|
||||||
|
<g id="cube_1">
|
||||||
|
<rect class="cls-1" x="76.7" y="25" width="249.85" height="292.37" rx="26" ry="26"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
34
docs/workers/prettier.js
Normal file
34
docs/workers/prettier.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// ref: https://github.com/nuxt/ui/blob/f3632ddee511f0fccb24d4fc37403421e84ffdae/docs/workers/prettier.js
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
self.onmessage = async function (event) {
|
||||||
|
self.postMessage({
|
||||||
|
uid: event.data.uid,
|
||||||
|
message: await handleMessage(event.data.message),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessage(message) {
|
||||||
|
switch (message.type) {
|
||||||
|
case 'format':
|
||||||
|
return handleFormatMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleFormatMessage(message) {
|
||||||
|
if (!globalThis.prettier) {
|
||||||
|
await Promise.all([
|
||||||
|
import('https://unpkg.com/prettier@3.3.3/standalone.js'),
|
||||||
|
import('https://unpkg.com/prettier@3.3.3/plugins/html.js'),
|
||||||
|
import('https://unpkg.com/prettier@3.3.3/plugins/markdown.js'),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
const { options, source } = message
|
||||||
|
const formatted = await prettier.format(source, {
|
||||||
|
parser: 'markdown',
|
||||||
|
plugins: prettierPlugins,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
}
|
@ -10,7 +10,7 @@ export default createConfigForNuxt({
|
|||||||
stylistic: true,
|
stylistic: true,
|
||||||
},
|
},
|
||||||
dirs: {
|
dirs: {
|
||||||
src: ['./playground'],
|
src: ['./playground', './docs'],
|
||||||
},
|
},
|
||||||
}).overrideRules({
|
}).overrideRules({
|
||||||
'@typescript-eslint/no-unused-expressions': [
|
'@typescript-eslint/no-unused-expressions': [
|
||||||
|
11
package.json
11
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rayine-ui",
|
"name": "rayine-ui",
|
||||||
"version": "1.3.2",
|
"version": "1.3.9",
|
||||||
"description": "RayineSoft UI Components",
|
"description": "RayineSoft UI Components",
|
||||||
"repository": "HoshinoSuzumi/rayine-ui",
|
"repository": "HoshinoSuzumi/rayine-ui",
|
||||||
"homepage": "https://rayui.uniiem.com",
|
"homepage": "https://rayui.uniiem.com",
|
||||||
@ -31,6 +31,9 @@
|
|||||||
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@iconify-json/svg-spinners": "^1.2.1",
|
||||||
|
"@iconify-json/tabler": "^1.2.8",
|
||||||
|
"@nuxt/icon": "^1.8.2",
|
||||||
"@nuxt/kit": "^3.14.159",
|
"@nuxt/kit": "^3.14.159",
|
||||||
"@nuxtjs/tailwindcss": "^6.12.2",
|
"@nuxtjs/tailwindcss": "^6.12.2",
|
||||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||||
@ -54,8 +57,12 @@
|
|||||||
"nuxt": "^3.14.159",
|
"nuxt": "^3.14.159",
|
||||||
"release-it": "^17.10.0",
|
"release-it": "^17.10.0",
|
||||||
"release-it-pnpm": "^4.6.3",
|
"release-it-pnpm": "^4.6.3",
|
||||||
"typescript": "latest",
|
"typescript": "^5.6.3",
|
||||||
"vitest": "^2.1.5",
|
"vitest": "^2.1.5",
|
||||||
"vue-tsc": "^2.1.10"
|
"vue-tsc": "^2.1.10"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"rayine-ui": "workspace:*",
|
||||||
|
"typescript": "5.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
1362
pnpm-lock.yaml
generated
1362
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -6,10 +6,11 @@ import {
|
|||||||
addPlugin,
|
addPlugin,
|
||||||
addComponentsDir,
|
addComponentsDir,
|
||||||
addImportsDir,
|
addImportsDir,
|
||||||
|
installModule,
|
||||||
} from '@nuxt/kit'
|
} from '@nuxt/kit'
|
||||||
import { name, version } from '../package.json'
|
import { name, version } from '../package.json'
|
||||||
import { installTailwind } from './tailwind'
|
import { installTailwind } from './tailwind'
|
||||||
import type { Strategy, DeepPartial } from './runtime/types/utils'
|
import type { Strategy, DeepPartial } from './runtime/types/index'
|
||||||
import { createTemplates } from './template'
|
import { createTemplates } from './template'
|
||||||
|
|
||||||
const _require = createRequire(import.meta.url)
|
const _require = createRequire(import.meta.url)
|
||||||
@ -65,6 +66,7 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
createTemplates(_nuxt)
|
createTemplates(_nuxt)
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
|
await installModule('@nuxt/icon')
|
||||||
installTailwind(_options, _nuxt, resolve)
|
installTailwind(_options, _nuxt, resolve)
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { twJoin, twMerge } from 'tailwind-merge'
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
||||||
import type { DeepPartial, Strategy } from '../../types/utils'
|
|
||||||
import type { ButtonColor, ButtonSize, ButtonVariant } from '../../types/button'
|
|
||||||
import { getNonUndefinedValuesFromObject } from '../../utils'
|
import { getNonUndefinedValuesFromObject } from '../../utils'
|
||||||
import { nuxtLinkProps } from '../../utils/link'
|
import { nuxtLinkProps } from '../../utils/link'
|
||||||
import { button } from '../../ui.config'
|
import { button } from '../../themes'
|
||||||
|
import type { ButtonColor, ButtonSize, ButtonVariant, DeepPartial, Strategy } from '../../types/index'
|
||||||
import { useRayUI } from '#build/imports'
|
import { useRayUI } from '#build/imports'
|
||||||
|
|
||||||
const config = button
|
const config = button
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
...nuxtLinkProps,
|
...nuxtLinkProps,
|
||||||
class: {
|
class: {
|
||||||
@ -61,6 +61,10 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: () => config.default.loadingIcon,
|
default: () => config.default.loadingIcon,
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
@ -78,6 +82,7 @@ export default defineComponent({
|
|||||||
ui.value.base,
|
ui.value.base,
|
||||||
ui.value.font,
|
ui.value.font,
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
|
ui.value.gap[props.size],
|
||||||
ui.value.size[props.size],
|
ui.value.size[props.size],
|
||||||
props.padded && ui.value.padding[props.size],
|
props.padded && ui.value.padding[props.size],
|
||||||
variant?.replaceAll('{color}', props.color),
|
variant?.replaceAll('{color}', props.color),
|
||||||
@ -85,12 +90,23 @@ export default defineComponent({
|
|||||||
), props.class)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const iconClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.icon.base,
|
||||||
|
ui.value.icon.size[props.size],
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const leadingIconName = computed(() => props.loading ? props.loadingIcon : props.icon)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
extProps,
|
extProps,
|
||||||
buttonClass,
|
buttonClass,
|
||||||
|
iconClass,
|
||||||
|
leadingIconName,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -99,7 +115,7 @@ export default defineComponent({
|
|||||||
<template>
|
<template>
|
||||||
<RayLink type="button" :disabled="disabled || loading" :class="buttonClass" v-bind="{ ...extProps, ...attrs }">
|
<RayLink type="button" :disabled="disabled || loading" :class="buttonClass" v-bind="{ ...extProps, ...attrs }">
|
||||||
<slot name="leading" :disabled="disabled" :loading="loading">
|
<slot name="leading" :disabled="disabled" :loading="loading">
|
||||||
<IconSpinner v-if="loading" class="mr-1" />
|
<RayIcon v-if="leadingIconName" :name="leadingIconName" :class="iconClass" />
|
||||||
</slot>
|
</slot>
|
||||||
<slot>
|
<slot>
|
||||||
<span v-if="label">{{ label }}</span>
|
<span v-if="label">{{ label }}</span>
|
||||||
|
31
src/runtime/components/elements/Icon.vue
Normal file
31
src/runtime/components/elements/Icon.vue
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, type PropType } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String as PropType<'svg' | 'css'>,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
customize: {
|
||||||
|
type: Function,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Icon v-bind="$props" />
|
||||||
|
</template>
|
63
src/runtime/components/elements/Kbd.vue
Normal file
63
src/runtime/components/elements/Kbd.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
||||||
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
|
import { kbd } from '../../themes'
|
||||||
|
import type { DeepPartial, KbdSize, Strategy } from '../../types'
|
||||||
|
import { useRayUI } from '#build/imports'
|
||||||
|
|
||||||
|
const config = kbd
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<KbdSize>,
|
||||||
|
default: config.default.size,
|
||||||
|
},
|
||||||
|
shadow: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const { ui, attrs } = useRayUI('kbd', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
|
const kbdClass = computed(() => {
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.base,
|
||||||
|
ui.value.background,
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.font,
|
||||||
|
ui.value.padding,
|
||||||
|
ui.value.ring,
|
||||||
|
props.shadow && ui.value.shadow,
|
||||||
|
ui.value.size[props.size],
|
||||||
|
), props.class)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
|
kbdClass,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<kbd :class="kbdClass" v-bind="attrs">
|
||||||
|
<slot>{{ label }}</slot>
|
||||||
|
</kbd>
|
||||||
|
</template>
|
90
src/runtime/components/elements/Mark.vue
Normal file
90
src/runtime/components/elements/Mark.vue
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
||||||
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
|
import { mark } from '../../themes'
|
||||||
|
import type { MarkColor, MarkPosition, MarkSize } from '../../types'
|
||||||
|
import { useRayUI } from '#build/imports'
|
||||||
|
|
||||||
|
const config = mark
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<MarkSize>,
|
||||||
|
default: config.default.size,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String as PropType<MarkColor>,
|
||||||
|
default: config.default.color,
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
type: String as PropType<MarkPosition>,
|
||||||
|
default: config.default.position,
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<typeof config>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const { ui, attrs } = useRayUI('mark', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
|
const markClass = computed(() => {
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.base,
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.ring,
|
||||||
|
ui.value.position[props.position],
|
||||||
|
ui.value.background.replaceAll('{color}', props.color),
|
||||||
|
props.value ? ui.value.value.size[props.size] : ui.value.size[props.size],
|
||||||
|
props.value ? ui.value.value.translate[props.position] : ui.value.translate[props.position],
|
||||||
|
), props.class)
|
||||||
|
})
|
||||||
|
|
||||||
|
const isOverMax = computed(() => {
|
||||||
|
if (props.max === null) return false
|
||||||
|
if (typeof props.value === 'string') return false
|
||||||
|
return props.value > props.max
|
||||||
|
})
|
||||||
|
|
||||||
|
// consider string value
|
||||||
|
const countValue = computed(() => {
|
||||||
|
if (typeof props.value === 'string') return props.value
|
||||||
|
return isOverMax.value ? `${props.max}+` : props.value
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
|
markClass,
|
||||||
|
isOverMax,
|
||||||
|
countValue,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="ui.wrapper">
|
||||||
|
<span :class="markClass">
|
||||||
|
<Transition v-bind="ui.transition">
|
||||||
|
<span v-if="value" :key="countValue" class="leading-none">{{ countValue }}</span>
|
||||||
|
</Transition>
|
||||||
|
</span>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -1,14 +1,164 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
||||||
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
|
import defu from 'defu'
|
||||||
|
import { input } from '../../themes'
|
||||||
|
import type { DeepPartial, InputColor, InputModelModifiers, InputSize, InputType, InputVariant, Strategy } from '../../types/index'
|
||||||
|
import { onMounted, ref, useRayUI } from '#build/imports'
|
||||||
|
|
||||||
|
const config = input
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Number] as PropType<string | number | null>,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String as PropType<InputType>,
|
||||||
|
default: 'text',
|
||||||
|
},
|
||||||
|
autofocus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
autofocusDelay: {
|
||||||
|
type: Number,
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
padded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<InputSize>,
|
||||||
|
default: () => config.default.size,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String as PropType<InputColor>,
|
||||||
|
default: () => config.default.color,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String as PropType<InputVariant>,
|
||||||
|
default: () => config.default.variant,
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
modelModifiers: {
|
||||||
|
type: Object as PropType<InputModelModifiers>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'change', 'blur'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { ui, attrs } = useRayUI('input', toRef(props, 'ui'), config)
|
||||||
|
const modelModifiers = ref(defu({}, props.modelModifiers, { lazy: false, number: false, trim: false }))
|
||||||
|
|
||||||
|
const input = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
|
const baseClass = computed(() => {
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.base,
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.placeholder,
|
||||||
|
ui.value.size[props.size],
|
||||||
|
props.padded && ui.value.padding[props.size],
|
||||||
|
ui.value.variant[props.variant].replaceAll('{color}', props.color),
|
||||||
|
), props.class)
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateValue = (value: string) => {
|
||||||
|
if (modelModifiers.value.trim) {
|
||||||
|
value = value.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelModifiers.value.number || props.type === 'number') {
|
||||||
|
const n = Number.parseFloat(value)
|
||||||
|
value = (Number.isNaN(n) ? value : n) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onInput = (e: Event) => {
|
||||||
|
if (modelModifiers.value.lazy) return
|
||||||
|
updateValue((e.target as HTMLInputElement).value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = (e: Event) => {
|
||||||
|
if (props.type === 'file') {
|
||||||
|
emit('change', (e.target as HTMLInputElement).files)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const value = (e.target as HTMLInputElement).value
|
||||||
|
emit('change', value)
|
||||||
|
if (modelModifiers.value.lazy) {
|
||||||
|
updateValue(value)
|
||||||
|
}
|
||||||
|
if (modelModifiers.value.trim) {
|
||||||
|
(e.target as HTMLInputElement).value = value.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onBlur = (e: Event) => {
|
||||||
|
emit('blur', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.autofocus) {
|
||||||
|
setTimeout(() => {
|
||||||
|
input.value?.focus()
|
||||||
|
}, props.autofocusDelay)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
|
baseClass,
|
||||||
|
input,
|
||||||
|
onInput,
|
||||||
|
onChange,
|
||||||
|
onBlur,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<input
|
<div :class="type === 'hidden' ? 'hidden' : ui.wrapper">
|
||||||
placeholder="test from rayine"
|
<input
|
||||||
class="rounded-lg border border-neutral-200 px-2 py-1"
|
ref="input"
|
||||||
>
|
:type="type"
|
||||||
|
:class="baseClass"
|
||||||
|
:disabled="disabled"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:required="required"
|
||||||
|
v-bind="type === 'file' ? attrs : { ...attrs, value: modelValue }"
|
||||||
|
@input="onInput"
|
||||||
|
@change="onChange"
|
||||||
|
@blur="onBlur"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
195
src/runtime/components/forms/Textarea.vue
Normal file
195
src/runtime/components/forms/Textarea.vue
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, onMounted, ref, toRef, watch, type PropType } from 'vue'
|
||||||
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
|
import defu from 'defu'
|
||||||
|
import { textarea } from '../../themes'
|
||||||
|
import type { DeepPartial, Strategy, TextareaColor, TextareaModelModifiers, TextareaSize, TextareaVariant } from '../../types'
|
||||||
|
import { useRayUI } from '#build/imports'
|
||||||
|
|
||||||
|
const config = textarea
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Number] as PropType<string | number | null>,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<TextareaSize>,
|
||||||
|
default: config.default.size,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String as PropType<TextareaVariant>,
|
||||||
|
default: config.default.variant,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String as PropType<TextareaColor>,
|
||||||
|
default: config.default.color,
|
||||||
|
},
|
||||||
|
autofocus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
autofocusDelay: {
|
||||||
|
type: Number,
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
rows: {
|
||||||
|
type: Number,
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
autosize: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
maxrows: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
padded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
resize: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
modelModifiers: {
|
||||||
|
type: Object as PropType<TextareaModelModifiers>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'update:modelValue',
|
||||||
|
'blur',
|
||||||
|
'change',
|
||||||
|
],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { ui, attrs } = useRayUI('textarea', toRef(props, 'ui'), config)
|
||||||
|
const modelModifiers = ref(defu({}, props.modelModifiers, { lazy: false, number: false, trim: false }))
|
||||||
|
|
||||||
|
const textarea = ref<HTMLTextAreaElement | null>(null)
|
||||||
|
|
||||||
|
const baseClass = computed(() => {
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.base,
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.placeholder,
|
||||||
|
ui.value.size[props.size],
|
||||||
|
props.padded && ui.value.padding[props.size],
|
||||||
|
ui.value.variant[props.variant].replaceAll('{color}', props.color),
|
||||||
|
!props.resize && 'resize-none',
|
||||||
|
), props.class)
|
||||||
|
})
|
||||||
|
|
||||||
|
const autoResize = () => {
|
||||||
|
if (!props.autosize) return
|
||||||
|
if (!textarea.value) return
|
||||||
|
textarea.value.rows = props.rows
|
||||||
|
const overflowBefore = textarea.value.style.overflow
|
||||||
|
textarea.value.style.overflow = 'hidden'
|
||||||
|
|
||||||
|
const style = window.getComputedStyle(textarea.value)
|
||||||
|
const padding = Number.parseInt(style.paddingTop) + Number.parseInt(style.paddingBottom)
|
||||||
|
const lineHeight = Number.parseInt(style.lineHeight)
|
||||||
|
const { scrollHeight, clientHeight } = textarea.value
|
||||||
|
const computedRows = Math.floor((scrollHeight - padding) / lineHeight)
|
||||||
|
if (computedRows > props.rows) {
|
||||||
|
textarea.value.rows = props.maxrows ? Math.min(computedRows, props.maxrows) : computedRows
|
||||||
|
}
|
||||||
|
textarea.value.style.overflow = overflowBefore
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateValue = (value: string) => {
|
||||||
|
if (modelModifiers.value.trim) {
|
||||||
|
value = value.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelModifiers.value.number) {
|
||||||
|
const n = Number.parseFloat(value)
|
||||||
|
value = (Number.isNaN(n) ? value : n) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onInput = (e: Event) => {
|
||||||
|
autoResize()
|
||||||
|
if (modelModifiers.value.lazy) return
|
||||||
|
updateValue((e.target as HTMLInputElement).value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = (e: Event) => {
|
||||||
|
const value = (e.target as HTMLInputElement).value
|
||||||
|
emit('change', value)
|
||||||
|
if (modelModifiers.value.lazy) {
|
||||||
|
updateValue(value)
|
||||||
|
}
|
||||||
|
if (modelModifiers.value.trim) {
|
||||||
|
(e.target as HTMLInputElement).value = value.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onBlur = (e: Event) => {
|
||||||
|
emit('blur', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.modelValue, () => {
|
||||||
|
autoResize()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.autofocus) {
|
||||||
|
setTimeout(() => {
|
||||||
|
textarea.value?.focus()
|
||||||
|
}, props.autofocusDelay)
|
||||||
|
}
|
||||||
|
autoResize()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
|
textarea,
|
||||||
|
baseClass,
|
||||||
|
onInput,
|
||||||
|
onChange,
|
||||||
|
onBlur,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="ui.wrapper">
|
||||||
|
<textarea
|
||||||
|
ref="textarea"
|
||||||
|
:class="baseClass"
|
||||||
|
:rows="rows"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
|
:value="modelValue"
|
||||||
|
v-bind="attrs"
|
||||||
|
@input="onInput"
|
||||||
|
@change="onChange"
|
||||||
|
@blur="onBlur"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
100
src/runtime/components/forms/Toggle.vue
Normal file
100
src/runtime/components/forms/Toggle.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
||||||
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
|
import { toggle } from '../../themes'
|
||||||
|
import type { DeepPartial, Strategy, ToggleColor, ToggleSize } from '../../types'
|
||||||
|
import { useRayUI } from '#build/imports'
|
||||||
|
|
||||||
|
const config = toggle
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean as PropType<boolean | null>,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
rounded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<ToggleSize>,
|
||||||
|
default: config.default.size,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String as PropType<ToggleColor>,
|
||||||
|
default: config.default.color,
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'update:modelValue',
|
||||||
|
'change',
|
||||||
|
],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { ui, attrs } = useRayUI('toggle', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
|
const checked = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value: boolean) => {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
emit('change', value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const toggleClass = computed(() => {
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.base,
|
||||||
|
props.rounded ? 'rounded-full' : ui.value.rounded,
|
||||||
|
ui.value.size[props.size],
|
||||||
|
ui.value.ring.replaceAll('{color}', props.color),
|
||||||
|
checked.value ? ui.value.active.replaceAll('{color}', props.color) : ui.value.inactive,
|
||||||
|
), props.class)
|
||||||
|
})
|
||||||
|
|
||||||
|
const bulletClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.bullet.base,
|
||||||
|
props.rounded ? 'rounded-full' : ui.value.bullet.rounded,
|
||||||
|
ui.value.bullet.shadow,
|
||||||
|
ui.value.bullet.size[props.size],
|
||||||
|
!props.disabled && ui.value.bullet.translate[props.size],
|
||||||
|
checked.value ? ui.value.bullet.active[props.size] : ui.value.bullet.inactive,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (!props.disabled) {
|
||||||
|
checked.value = !checked.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
|
checked,
|
||||||
|
toggleClass,
|
||||||
|
bulletClass,
|
||||||
|
handleClick,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button :class="toggleClass" :disabled="disabled" v-bind="attrs" @click="handleClick">
|
||||||
|
<span :class="bulletClass" />
|
||||||
|
</button>
|
||||||
|
</template>
|
@ -1,9 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, onMounted, defineComponent, type PropType, toRef, computed } from 'vue'
|
import { onMounted, defineComponent, type PropType, toRef, computed } from 'vue'
|
||||||
import { twJoin, twMerge } from 'tailwind-merge'
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
import type { Message, MessageColor, MessageType } from '../../types/message'
|
import { message } from '../../themes'
|
||||||
import { message } from '../../ui.config'
|
import type { DeepPartial, Message, MessageColor, MessageType, Strategy } from '../../types/index'
|
||||||
import type { DeepPartial, Strategy } from '../../types'
|
|
||||||
import { useMessage, useRayUI } from '#build/imports'
|
import { useMessage, useRayUI } from '#build/imports'
|
||||||
|
|
||||||
const config = message
|
const config = message
|
||||||
@ -18,6 +17,10 @@ export default defineComponent({
|
|||||||
type: String as PropType<MessageColor>,
|
type: String as PropType<MessageColor>,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
duration: {
|
duration: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: config.default.duration,
|
default: config.default.duration,
|
||||||
@ -64,6 +67,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const iconName = computed(() => {
|
||||||
|
return props.icon || ui.value.type[props.type]?.icon || null
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
message.remove(messageBody.value.id)
|
message.remove(messageBody.value.id)
|
||||||
@ -76,6 +83,7 @@ export default defineComponent({
|
|||||||
attrs,
|
attrs,
|
||||||
messageBody,
|
messageBody,
|
||||||
containerClass,
|
containerClass,
|
||||||
|
iconName,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -84,10 +92,7 @@ export default defineComponent({
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper" v-bind="attrs">
|
<div :class="ui.wrapper" v-bind="attrs">
|
||||||
<div :class="containerClass">
|
<div :class="containerClass">
|
||||||
<IconCircleSuccess v-if="messageBody?.type === 'success'" class="text-xl" />
|
<RayIcon v-if="iconName" :name="iconName" class="text-xl" />
|
||||||
<IconCircleWarning v-if="messageBody?.type === 'warning'" class="text-xl" />
|
|
||||||
<IconCircleError v-if="messageBody?.type === 'error'" class="text-xl" />
|
|
||||||
<IconCircleInfo v-if="messageBody?.type === 'info'" class="text-xl" />
|
|
||||||
<span>
|
<span>
|
||||||
{{ messageBody.content }}
|
{{ messageBody.content }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, ref, toRef, type PropType } from 'vue'
|
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
||||||
import { twJoin, twMerge } from 'tailwind-merge'
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
import type { Message, MessageType } from '../../types/message'
|
import { messages } from '../../themes'
|
||||||
import { messages } from '../../ui.config'
|
import type { DeepPartial, Message, Strategy } from '../../types/index'
|
||||||
import type { DeepPartial, Strategy } from '../../types'
|
|
||||||
import { useState } from '#imports'
|
import { useState } from '#imports'
|
||||||
import { useRayUI } from '#build/imports'
|
import { useRayUI } from '#build/imports'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Message, MessageType } from '../types/message'
|
import type { Message } from '../types/message'
|
||||||
import { useState } from '#imports'
|
import { useState } from '#imports'
|
||||||
|
|
||||||
export const useMessage = () => {
|
export const useMessage = () => {
|
||||||
|
@ -1,40 +1,29 @@
|
|||||||
|
import { standard } from '..'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
base: 'focus:outline-none focus-visible:outline-0 disabled:cursor-not-allowed disabled:opacity-70 aria-disabled:cursor-not-allowed aria-disabled:opacity-70 flex-shrink-0 transition',
|
base: 'focus:outline-none focus-visible:outline-0 disabled:cursor-not-allowed disabled:opacity-70 aria-disabled:cursor-not-allowed aria-disabled:opacity-70 flex-shrink-0 transition text-left break-all line-clamp-1',
|
||||||
rounded: 'rounded-lg',
|
rounded: 'rounded-lg',
|
||||||
font: 'font-medium',
|
font: 'font-medium',
|
||||||
block: 'w-full flex justify-center items-center',
|
block: 'w-full flex justify-center items-center',
|
||||||
inline: 'inline-flex items-center',
|
inline: 'inline-flex items-center',
|
||||||
size: {
|
size: {
|
||||||
'2xs': 'text-xs',
|
...standard.size,
|
||||||
'xs': 'text-xs',
|
|
||||||
'sm': 'text-sm',
|
|
||||||
'md': 'text-sm',
|
|
||||||
'lg': 'text-sm',
|
|
||||||
'xl': 'text-base',
|
|
||||||
},
|
},
|
||||||
padding: {
|
padding: {
|
||||||
'2xs': 'px-2 py-1',
|
...standard.padding,
|
||||||
'xs': 'px-2.5 py-1.5',
|
|
||||||
'sm': 'px-2.5 py-1.5',
|
|
||||||
'md': 'px-3 py-2',
|
|
||||||
'lg': 'px-3.5 py-2.5',
|
|
||||||
'xl': 'px-3.5 py-2.5',
|
|
||||||
},
|
},
|
||||||
square: {
|
square: {
|
||||||
'2xs': 'p-1',
|
...standard.square,
|
||||||
'xs': 'p-1.5',
|
},
|
||||||
'sm': 'p-1.5',
|
gap: {
|
||||||
'md': 'p-2',
|
...standard.gap,
|
||||||
'lg': 'p-2.5',
|
|
||||||
'xl': 'p-2.5',
|
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
base: 'flex-shrink-0',
|
base: 'flex-shrink-0',
|
||||||
loading: 'animate-spin',
|
|
||||||
size: {
|
size: {
|
||||||
'2xs': 'h-4 w-4',
|
'2xs': 'h-4 w-4',
|
||||||
'xs': 'h-4 w-4',
|
'xs': 'h-4 w-4',
|
||||||
'sm': 'h-5 w-5',
|
'sm': 'h-4 w-4',
|
||||||
'md': 'h-5 w-5',
|
'md': 'h-5 w-5',
|
||||||
'lg': 'h-5 w-5',
|
'lg': 'h-5 w-5',
|
||||||
'xl': 'h-6 w-6',
|
'xl': 'h-6 w-6',
|
||||||
@ -64,6 +53,6 @@ export default {
|
|||||||
size: 'sm',
|
size: 'sm',
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
loadingIcon: 'loading',
|
loadingIcon: 'svg-spinners:90-ring-with-bg',
|
||||||
},
|
},
|
||||||
}
|
}
|
17
src/runtime/themes/elements/kbd.ts
Normal file
17
src/runtime/themes/elements/kbd.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export default {
|
||||||
|
base: 'inline-flex justify-center items-center text-gray-900 dark:text-gray-100 leading-none',
|
||||||
|
rounded: 'rounded',
|
||||||
|
font: 'font-medium',
|
||||||
|
background: 'bg-gray-100 dark:bg-gray-800',
|
||||||
|
ring: 'ring-1 ring-inset ring-gray-300 dark:ring-gray-700',
|
||||||
|
padding: 'px-1',
|
||||||
|
shadow: 'shadow-sm',
|
||||||
|
size: {
|
||||||
|
xs: 'h-4 min-w-4 text-[10px]',
|
||||||
|
sm: 'h-5 min-w-5 text-[11px]',
|
||||||
|
md: 'h-6 min-w-6 text-xs',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
size: 'sm',
|
||||||
|
},
|
||||||
|
}
|
51
src/runtime/themes/elements/mark.ts
Normal file
51
src/runtime/themes/elements/mark.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
export default {
|
||||||
|
wrapper: 'relative',
|
||||||
|
base: 'absolute text-white rounded-full inline-flex justify-center items-center',
|
||||||
|
ring: 'ring-2 ring-white dark:ring-gray-900',
|
||||||
|
rounded: 'rounded-full',
|
||||||
|
background: 'bg-{color}-500',
|
||||||
|
position: {
|
||||||
|
'top-left': 'top-0 left-0',
|
||||||
|
'top-right': 'top-0 right-0',
|
||||||
|
'bottom-left': 'bottom-0 left-0',
|
||||||
|
'bottom-right': 'bottom-0 right-0',
|
||||||
|
},
|
||||||
|
translate: {
|
||||||
|
'top-left': '-translate-x-0.5 -translate-y-0.5',
|
||||||
|
'top-right': 'translate-x-0.5 -translate-y-0.5',
|
||||||
|
'bottom-left': '-translate-x-0.5 translate-y-0.5',
|
||||||
|
'bottom-right': 'translate-x-0.5 translate-y-0.5',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
xs: 'w-1.5 h-1.5',
|
||||||
|
sm: 'w-2 h-2',
|
||||||
|
md: 'w-2.5 h-2.5',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
size: {
|
||||||
|
xs: 'px-1 h-3 leading-none text-xs',
|
||||||
|
sm: 'px-1.5 h-4 leading-none text-xs',
|
||||||
|
md: 'px-2 h-5 leading-none text-sm',
|
||||||
|
},
|
||||||
|
translate: {
|
||||||
|
'top-left': '-translate-x-1/3 -translate-y-1/3',
|
||||||
|
'top-right': 'translate-x-1/3 -translate-y-1/3',
|
||||||
|
'bottom-left': '-translate-x-1/3 translate-y-1/3',
|
||||||
|
'bottom-right': 'translate-x-1/3 translate-y-1/3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transition: {
|
||||||
|
moveClass: 'transform ease-out duration-300 transition',
|
||||||
|
enterActiveClass: 'transform ease-out duration-300 transition',
|
||||||
|
leaveActiveClass: 'transform ease-out duration-300 transition absolute',
|
||||||
|
enterFromClass: 'translate-y-2 opacity-0',
|
||||||
|
enterToClass: 'translate-y-0 opacity-100',
|
||||||
|
leaveFromClass: 'translate-y-0 opacity-100',
|
||||||
|
leaveToClass: '-translate-y-2 opacity-0',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
size: 'sm',
|
||||||
|
color: 'primary',
|
||||||
|
position: 'top-right',
|
||||||
|
},
|
||||||
|
}
|
23
src/runtime/themes/forms/input.ts
Normal file
23
src/runtime/themes/forms/input.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { standard } from '..'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
wrapper: 'relative',
|
||||||
|
base: 'relative w-full block focus:outline-none disabled:cursor-not-allowed disabled:opacity-70 transition',
|
||||||
|
placeholder: 'placeholder:text-gray-400 dark:placeholder:text-gray-500',
|
||||||
|
rounded: 'rounded-md',
|
||||||
|
size: {
|
||||||
|
...standard.size,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
...standard.padding,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
outline: 'shadow-sm bg-transparent text-gray-900 dark:text-white ring ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-{color}-500 dark:focus:ring-{color}-400',
|
||||||
|
plain: 'bg-transparent',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
size: 'sm',
|
||||||
|
color: 'primary',
|
||||||
|
variant: 'outline',
|
||||||
|
},
|
||||||
|
}
|
24
src/runtime/themes/forms/textarea.ts
Normal file
24
src/runtime/themes/forms/textarea.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { standard } from '..'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
wrapper: 'relative',
|
||||||
|
base: 'relative w-full block focus:outline-none disabled:cursor-not-allowed disabled:opacity-70 transition',
|
||||||
|
placeholder: 'placeholder:text-gray-400 dark:placeholder:text-gray-500',
|
||||||
|
rounded: 'rounded-md',
|
||||||
|
size: {
|
||||||
|
...standard.size,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
...standard.padding,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
outline:
|
||||||
|
'shadow-sm bg-transparent text-gray-900 dark:text-white ring ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-{color}-500 dark:focus:ring-{color}-400',
|
||||||
|
plain: 'bg-transparent',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
size: 'sm',
|
||||||
|
color: 'primary',
|
||||||
|
variant: 'outline',
|
||||||
|
},
|
||||||
|
}
|
53
src/runtime/themes/forms/toggle.ts
Normal file
53
src/runtime/themes/forms/toggle.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
export default {
|
||||||
|
base: 'relative inline-flex flex-shrink-0 shadow-inner disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none transition ease-in-out duration-200 group',
|
||||||
|
rounded: 'rounded-md',
|
||||||
|
ring: 'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-{color}-500 dark:focus-visible:ring-{color}-400 focus-visible:ring-offset-white dark:focus-visible:ring-offset-gray-900',
|
||||||
|
active: 'bg-{color}-500 dark:bg-{color}-400',
|
||||||
|
inactive: 'bg-gray-100 dark:bg-gray-800',
|
||||||
|
size: {
|
||||||
|
'2xs': 'h-3 w-5',
|
||||||
|
'xs': 'h-3.5 w-6',
|
||||||
|
'sm': 'h-4 w-7',
|
||||||
|
'md': 'h-5 w-9',
|
||||||
|
'lg': 'h-6 w-11',
|
||||||
|
'xl': 'h-7 w-[3.25rem]',
|
||||||
|
'2xl': 'h-8 w-[3.75rem]',
|
||||||
|
},
|
||||||
|
bullet: {
|
||||||
|
base: 'relative inline-block m-0.5 bg-white dark:bg-gray-900 pointer-events-none transform transition ease-in-out duration-300 group-active:scale-90 group-disabled:scale-100',
|
||||||
|
shadow: 'shadow',
|
||||||
|
rounded: 'rounded',
|
||||||
|
size: {
|
||||||
|
'2xs': 'h-2 w-2',
|
||||||
|
'xs': 'h-2.5 w-2.5',
|
||||||
|
'sm': 'h-3 w-3',
|
||||||
|
'md': 'h-4 w-4',
|
||||||
|
'lg': 'h-5 w-5',
|
||||||
|
'xl': 'h-6 w-6',
|
||||||
|
'2xl': 'h-7 w-7',
|
||||||
|
},
|
||||||
|
translate: {
|
||||||
|
'2xs': 'group-active:translate-x-1',
|
||||||
|
'xs': 'group-active:translate-x-1.5',
|
||||||
|
'sm': 'group-active:translate-x-2',
|
||||||
|
'md': 'group-active:translate-x-2.5',
|
||||||
|
'lg': 'group-active:translate-x-3',
|
||||||
|
'xl': 'group-active:translate-x-3.5',
|
||||||
|
'2xl': 'group-active:translate-x-4',
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
'2xs': 'translate-x-2',
|
||||||
|
'xs': 'translate-x-2.5',
|
||||||
|
'sm': 'translate-x-3',
|
||||||
|
'md': 'translate-x-4',
|
||||||
|
'lg': 'translate-x-5',
|
||||||
|
'xl': 'translate-x-6',
|
||||||
|
'2xl': 'translate-x-7',
|
||||||
|
},
|
||||||
|
inactive: 'translate-x-0',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
size: 'md',
|
||||||
|
color: 'primary',
|
||||||
|
},
|
||||||
|
}
|
16
src/runtime/themes/index.ts
Normal file
16
src/runtime/themes/index.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// universal
|
||||||
|
export { default as standard } from './standard'
|
||||||
|
|
||||||
|
// elements
|
||||||
|
export { default as button } from './elements/button'
|
||||||
|
export { default as kbd } from './elements/kbd'
|
||||||
|
export { default as mark } from './elements/mark'
|
||||||
|
|
||||||
|
// forms
|
||||||
|
export { default as input } from './forms/input'
|
||||||
|
export { default as textarea } from './forms/textarea'
|
||||||
|
export { default as toggle } from './forms/toggle'
|
||||||
|
|
||||||
|
// overlays
|
||||||
|
export { default as message } from './overlays/message'
|
||||||
|
export { default as messages } from './overlays/messages'
|
@ -9,15 +9,19 @@ export default {
|
|||||||
type: {
|
type: {
|
||||||
success: {
|
success: {
|
||||||
color: 'emerald',
|
color: 'emerald',
|
||||||
|
icon: 'tabler:circle-check',
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
color: 'amber',
|
color: 'amber',
|
||||||
|
icon: 'tabler:alert-circle',
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
color: 'red',
|
color: 'red',
|
||||||
|
icon: 'tabler:circle-x',
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
color: 'blue',
|
color: 'blue',
|
||||||
|
icon: 'tabler:info-circle',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
34
src/runtime/themes/standard.ts
Normal file
34
src/runtime/themes/standard.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export default {
|
||||||
|
size: {
|
||||||
|
'2xs': 'text-xs',
|
||||||
|
'xs': 'text-xs',
|
||||||
|
'sm': 'text-sm',
|
||||||
|
'md': 'text-sm',
|
||||||
|
'lg': 'text-sm',
|
||||||
|
'xl': 'text-base',
|
||||||
|
},
|
||||||
|
gap: {
|
||||||
|
'2xs': 'gap-x-1',
|
||||||
|
'xs': 'gap-x-1.5',
|
||||||
|
'sm': 'gap-x-1.5',
|
||||||
|
'md': 'gap-x-2',
|
||||||
|
'lg': 'gap-x-2.5',
|
||||||
|
'xl': 'gap-x-2.5',
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
'2xs': 'px-2 py-1',
|
||||||
|
'xs': 'px-2.5 py-1.5',
|
||||||
|
'sm': 'px-2.5 py-1.5',
|
||||||
|
'md': 'px-3 py-2',
|
||||||
|
'lg': 'px-3.5 py-2.5',
|
||||||
|
'xl': 'px-3.5 py-2.5',
|
||||||
|
},
|
||||||
|
square: {
|
||||||
|
'2xs': 'p-1',
|
||||||
|
'xs': 'p-1.5',
|
||||||
|
'sm': 'p-1.5',
|
||||||
|
'md': 'p-2',
|
||||||
|
'lg': 'p-2.5',
|
||||||
|
'xl': 'p-2.5',
|
||||||
|
},
|
||||||
|
} as const
|
2
src/runtime/types/button.d.ts
vendored
2
src/runtime/types/button.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
import type { AppConfig } from 'nuxt/schema'
|
import type { AppConfig } from 'nuxt/schema'
|
||||||
import type { button } from '../ui.config'
|
import type { button } from '../themes'
|
||||||
import type { ExtractDeepObject, NestedKeyOf, ExtractDeepKey } from './utils'
|
import type { ExtractDeepObject, NestedKeyOf, ExtractDeepKey } from './utils'
|
||||||
import type colors from '#ray-colors'
|
import type colors from '#ray-colors'
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
export * from './button'
|
export * from './button'
|
||||||
export * from './message'
|
export * from './message'
|
||||||
|
export * from './input'
|
||||||
|
export * from './textarea'
|
||||||
|
export * from './kbd'
|
||||||
|
export * from './toggle'
|
||||||
|
export * from './mark'
|
||||||
|
|
||||||
export * from './utils'
|
export * from './utils'
|
||||||
|
20
src/runtime/types/input.d.ts
vendored
Normal file
20
src/runtime/types/input.d.ts
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { AppConfig } from 'nuxt/schema'
|
||||||
|
import type { input } from '../themes'
|
||||||
|
import type { ExtractDeepKey } from './utils'
|
||||||
|
import type colors from '#ray-colors'
|
||||||
|
|
||||||
|
export type InputSize =
|
||||||
|
| keyof typeof input.size
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'input', 'size']>
|
||||||
|
export type InputColor =
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'input', 'color']>
|
||||||
|
| (typeof colors)[number]
|
||||||
|
export type InputVariant =
|
||||||
|
| keyof typeof input.variant
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'input', 'variant']>
|
||||||
|
export type InputType = 'text' | 'password' | 'number' | 'url' | 'email' | 'search' | 'file' | 'hidden'
|
||||||
|
export type InputModelModifiers = {
|
||||||
|
number?: boolean
|
||||||
|
trim?: boolean
|
||||||
|
lazy?: boolean
|
||||||
|
}
|
7
src/runtime/types/kbd.d.ts
vendored
Normal file
7
src/runtime/types/kbd.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { AppConfig } from 'nuxt/schema'
|
||||||
|
import type { kbd } from '../themes'
|
||||||
|
import type { ExtractDeepKey } from './utils'
|
||||||
|
|
||||||
|
export type KbdSize =
|
||||||
|
| keyof typeof kbd.size
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'kbd', 'size']>
|
14
src/runtime/types/mark.d.ts
vendored
Normal file
14
src/runtime/types/mark.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
|
import type { mark } from '../themes'
|
||||||
|
import type { ExtractDeepKey } from './utils'
|
||||||
|
import type colors from '#ray-colors'
|
||||||
|
|
||||||
|
export type MarkSize =
|
||||||
|
| keyof typeof mark.size
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'mark', 'size']>
|
||||||
|
export type MarkColor =
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'mark', 'color']>
|
||||||
|
| (typeof colors)[number]
|
||||||
|
export type MarkPosition =
|
||||||
|
| keyof typeof mark.position
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'mark', 'position']>
|
2
src/runtime/types/message.d.ts
vendored
2
src/runtime/types/message.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
import type { AppConfig } from 'nuxt/schema'
|
import type { AppConfig } from 'nuxt/schema'
|
||||||
import type { message } from '../ui.config'
|
import type { message } from '../themes'
|
||||||
import type colors from '#ray-colors'
|
import type colors from '#ray-colors'
|
||||||
|
|
||||||
export type MessageType = keyof typeof message.type
|
export type MessageType = keyof typeof message.type
|
||||||
|
19
src/runtime/types/textarea.d.ts
vendored
Normal file
19
src/runtime/types/textarea.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
|
import type { textarea } from '../themes'
|
||||||
|
import type { ExtractDeepKey } from './utils'
|
||||||
|
import type colors from '#ray-colors'
|
||||||
|
|
||||||
|
export type TextareaSize =
|
||||||
|
| keyof typeof textarea.size
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'textarea', 'size']>
|
||||||
|
export type TextareaColor =
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'textarea', 'color']>
|
||||||
|
| (typeof colors)[number]
|
||||||
|
export type TextareaVariant =
|
||||||
|
| keyof typeof textarea.variant
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'textarea', 'variant']>
|
||||||
|
export type TextareaModelModifiers = {
|
||||||
|
number?: boolean
|
||||||
|
trim?: boolean
|
||||||
|
lazy?: boolean
|
||||||
|
}
|
9
src/runtime/types/toggle.d.ts
vendored
Normal file
9
src/runtime/types/toggle.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
|
import type { toggle } from '../themes'
|
||||||
|
import type { ExtractDeepKey } from './utils'
|
||||||
|
import type colors from '#ray-colors'
|
||||||
|
|
||||||
|
export type ToggleSize =
|
||||||
|
| keyof typeof toggle.size
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'toggle', 'size']>
|
||||||
|
export type ToggleColor = (typeof colors)[number]
|
@ -1,6 +0,0 @@
|
|||||||
// elements
|
|
||||||
export { default as button } from './elements/button'
|
|
||||||
|
|
||||||
// overlays
|
|
||||||
export { default as message } from './overlays/message'
|
|
||||||
export { default as messages } from './overlays/messages'
|
|
@ -161,6 +161,34 @@ const safelistForComponent: Record<
|
|||||||
variants: ['dark'],
|
variants: ['dark'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
input: colorsToRegex => [
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^text-(${colorsToRegex})-400$`),
|
||||||
|
variants: ['placeholder'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^text-(${colorsToRegex})-500$`),
|
||||||
|
variants: ['dark:placeholder'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^ring-(${colorsToRegex})-400$`),
|
||||||
|
variants: ['dark:focus'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^ring-(${colorsToRegex})-500$`),
|
||||||
|
variants: ['focus'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toggle: colorsToRegex => [
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^ring-(${colorsToRegex})-400$`),
|
||||||
|
variants: ['focus-visible', 'dark'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^ring-(${colorsToRegex})-500$`),
|
||||||
|
variants: ['focus-visible'],
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateSafelist = (colors: string[], globalColors: string[]) => {
|
export const generateSafelist = (colors: string[], globalColors: string[]) => {
|
||||||
|
@ -49,7 +49,7 @@ export const installTailwind = (
|
|||||||
resolve(runtimePath, 'components/**/*.{vue,mjs,ts}'),
|
resolve(runtimePath, 'components/**/*.{vue,mjs,ts}'),
|
||||||
)},
|
)},
|
||||||
${JSON.stringify(
|
${JSON.stringify(
|
||||||
resolve(runtimePath, 'ui.config/**/*.{mjs,js,ts}'),
|
resolve(runtimePath, 'themes/**/*.{mjs,js,ts}'),
|
||||||
)}
|
)}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user