mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-24 08:46:15 -04:00
feat(ui-lib): demo pages for c-lib components
This commit is contained in:
parent
e88c1d5f2c
commit
92bd83536f
14 changed files with 294 additions and 248 deletions
|
@ -23,9 +23,9 @@ export const config = figue({
|
|||
env: {
|
||||
doc: 'Application current env',
|
||||
format: 'enum',
|
||||
values: ['production', 'development', 'test'],
|
||||
values: ['production', 'development', 'preview', 'test'],
|
||||
default: 'development',
|
||||
env: 'MODE',
|
||||
env: 'VITE_VERCEL_ENV',
|
||||
},
|
||||
},
|
||||
plausible: {
|
||||
|
|
|
@ -4,6 +4,7 @@ import HomePage from './pages/Home.page.vue';
|
|||
import NotFound from './pages/404.page.vue';
|
||||
import { tools } from './tools';
|
||||
import { config } from './config';
|
||||
import { routes as demoRoutes } from './ui/demo/demo.routes';
|
||||
|
||||
const toolsRoutes = tools.map(({ path, name, component, ...config }) => ({
|
||||
path,
|
||||
|
@ -32,6 +33,7 @@ const router = createRouter({
|
|||
},
|
||||
...toolsRoutes,
|
||||
...toolsRedirectRoutes,
|
||||
...(config.app.env === 'development' ? demoRoutes : []),
|
||||
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
|
||||
],
|
||||
});
|
||||
|
|
29
src/ui/c-button/c-button.demo.vue
Normal file
29
src/ui/c-button/c-button.demo.vue
Normal file
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<div v-for="buttonVariant of buttonVariants" :key="buttonVariant">
|
||||
<h2>{{ _.capitalize(buttonVariant) }}</h2>
|
||||
|
||||
<c-button v-for="buttonType of buttonTypes" :key="buttonType" :variant="buttonVariant" :type="buttonType" mx-1>
|
||||
Button
|
||||
</c-button>
|
||||
|
||||
<c-button
|
||||
v-for="buttonType of buttonTypes"
|
||||
:key="buttonType"
|
||||
:variant="buttonVariant"
|
||||
:type="buttonType"
|
||||
circle
|
||||
mx-1
|
||||
>
|
||||
A
|
||||
</c-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import _ from 'lodash';
|
||||
|
||||
const buttonVariants = ['basic', 'text'];
|
||||
const buttonTypes = ['default', 'primary'];
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -1,240 +1,76 @@
|
|||
import { darken, lighten } from '../color/color.models';
|
||||
import { defineThemes } from '../theme/theme.models';
|
||||
import { appThemes } from '../theme/themes';
|
||||
|
||||
export const { useTheme } = defineThemes({
|
||||
dark: {
|
||||
basic: {
|
||||
default: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.12)',
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.24)',
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.dark.primary.color,
|
||||
},
|
||||
},
|
||||
|
||||
primary: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.primary.color,
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.primary.colorHover,
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.primary.colorPressed,
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.dark.primary.color,
|
||||
},
|
||||
},
|
||||
|
||||
warning: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.warning.color,
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.warning.colorHover,
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.warning.colorPressed,
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.dark.warning.color,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
text: {
|
||||
default: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: 'transparent',
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.12)',
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.82)',
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.dark.primary.color,
|
||||
},
|
||||
},
|
||||
|
||||
primary: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.primary.color,
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.primary.colorHover,
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.primary.colorPressed,
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.dark.primary.color,
|
||||
},
|
||||
},
|
||||
|
||||
warning: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.warning.color,
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.warning.colorHover,
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.dark.warning.colorPressed,
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.dark.warning.color,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
light: {
|
||||
basic: {
|
||||
default: {
|
||||
textColor: appThemes.light.text.baseColor,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.05)',
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.light.text.baseColor,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.09)',
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.light.text.baseColor,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.22)',
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.light.primary.color,
|
||||
},
|
||||
},
|
||||
|
||||
primary: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.light.primary.color,
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.light.primary.colorHover,
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.light.primary.colorPressed,
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.light.primary.color,
|
||||
},
|
||||
},
|
||||
|
||||
warning: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.light.warning.color,
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.light.warning.colorHover,
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.dark.text.baseColor,
|
||||
backgroundColor: appThemes.light.warning.colorPressed,
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.light.warning.color,
|
||||
},
|
||||
},
|
||||
},
|
||||
text: {
|
||||
default: {
|
||||
textColor: appThemes.light.text.baseColor,
|
||||
backgroundColor: 'transparent',
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.light.text.baseColor,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.09)',
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.light.text.baseColor,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.13)',
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.light.primary.color,
|
||||
},
|
||||
},
|
||||
primary: {
|
||||
textColor: appThemes.light.primary.color,
|
||||
backgroundColor: 'transparent',
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.light.primary.colorHover,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.09)',
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.light.primary.colorPressed,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.13)',
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.light.primary.color,
|
||||
},
|
||||
},
|
||||
warning: {
|
||||
textColor: appThemes.light.warning.color,
|
||||
backgroundColor: 'transparent',
|
||||
|
||||
hover: {
|
||||
textColor: appThemes.light.warning.colorHover,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.09)',
|
||||
},
|
||||
|
||||
pressed: {
|
||||
textColor: appThemes.light.warning.colorPressed,
|
||||
backgroundColor: 'rgba(46, 51, 56, 0.13)',
|
||||
},
|
||||
|
||||
outline: {
|
||||
color: appThemes.light.warning.color,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
const createState = ({
|
||||
textColor,
|
||||
backgroundColor,
|
||||
hoverBackground,
|
||||
hoveredTextColor = textColor,
|
||||
pressedBackground,
|
||||
pressedTextColor = textColor,
|
||||
}: {
|
||||
textColor: string;
|
||||
backgroundColor: string;
|
||||
hoverBackground: string;
|
||||
hoveredTextColor?: string;
|
||||
pressedBackground: string;
|
||||
pressedTextColor?: string;
|
||||
}) => ({
|
||||
textColor,
|
||||
backgroundColor,
|
||||
hover: { textColor: hoveredTextColor, backgroundColor: hoverBackground },
|
||||
pressed: { textColor: pressedTextColor, backgroundColor: pressedBackground },
|
||||
});
|
||||
|
||||
const createTheme = ({ style }: { style: 'light' | 'dark' }) => {
|
||||
const theme = appThemes[style];
|
||||
|
||||
return {
|
||||
basic: {
|
||||
default: createState({
|
||||
textColor: theme.text.baseColor,
|
||||
backgroundColor: theme.default.color,
|
||||
hoverBackground: theme.default.colorHover,
|
||||
pressedBackground: theme.default.colorPressed,
|
||||
}),
|
||||
primary: createState({
|
||||
textColor: theme.primary.color,
|
||||
backgroundColor: theme.primary.colorFaded,
|
||||
hoverBackground: lighten(theme.primary.colorFaded, 30),
|
||||
pressedBackground: darken(theme.primary.colorFaded, 30),
|
||||
}),
|
||||
warning: createState({
|
||||
textColor: theme.text.baseColor,
|
||||
backgroundColor: theme.warning.color,
|
||||
hoverBackground: theme.warning.colorHover,
|
||||
pressedBackground: theme.warning.colorPressed,
|
||||
}),
|
||||
},
|
||||
text: {
|
||||
default: createState({
|
||||
textColor: theme.text.baseColor,
|
||||
backgroundColor: 'transparent',
|
||||
hoverBackground: theme.default.colorHover,
|
||||
pressedBackground: theme.default.colorPressed,
|
||||
}),
|
||||
primary: createState({
|
||||
textColor: theme.primary.color,
|
||||
backgroundColor: 'transparent',
|
||||
hoverBackground: theme.primary.colorFaded,
|
||||
pressedBackground: darken(theme.primary.colorFaded, 30),
|
||||
}),
|
||||
warning: createState({
|
||||
textColor: theme.text.baseColor,
|
||||
backgroundColor: theme.warning.color,
|
||||
hoverBackground: theme.warning.colorHover,
|
||||
pressedBackground: theme.warning.colorPressed,
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const { useTheme } = defineThemes({
|
||||
dark: createTheme({ style: 'dark' }),
|
||||
light: createTheme({ style: 'light' }),
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<script lang="ts" setup>
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
import { useTheme } from './c-button.theme';
|
||||
import { useAppTheme } from '../theme/themes';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -56,11 +57,11 @@ const tag = computed(() => {
|
|||
}
|
||||
return 'button';
|
||||
});
|
||||
const appTheme = useAppTheme();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.c-button {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
|
@ -103,7 +104,7 @@ const tag = computed(() => {
|
|||
}
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid v-bind('variantTheme.outline.color');
|
||||
outline: 1px solid v-bind('appTheme.primary.color');
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
|
|
13
src/ui/c-card/c-card.demo.vue
Normal file
13
src/ui/c-card/c-card.demo.vue
Normal file
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
<h2>Default</h2>
|
||||
|
||||
<c-card title="Title">
|
||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Repudiandae ipsa reiciendis facilis officia nulla.
|
||||
Laboriosam cumque molestias excepturi doloribus nulla nemo quod ratione rerum possimus. Excepturi nihil possimus
|
||||
error itaque.
|
||||
</c-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
12
src/ui/c-link/c-link.demo.vue
Normal file
12
src/ui/c-link/c-link.demo.vue
Normal file
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<h2>Default</h2>
|
||||
<c-link mx-1> Link </c-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import CLink from './c-link.vue';
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -16,7 +16,15 @@ const props = defineProps<{
|
|||
const { href, to } = toRefs(props);
|
||||
|
||||
const theme = useTheme();
|
||||
const tag = computed(() => (href?.value ? 'a' : RouterLink));
|
||||
const tag = computed(() => {
|
||||
if (href?.value) {
|
||||
return 'a';
|
||||
}
|
||||
if (to?.value) {
|
||||
return RouterLink;
|
||||
}
|
||||
return 'span';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
40
src/ui/color/color.models.test.ts
Normal file
40
src/ui/color/color.models.test.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { darken, lighten, setOpacity } from './color.models';
|
||||
|
||||
describe('color models', () => {
|
||||
describe('lighten', () => {
|
||||
test('lightens a color', () => {
|
||||
expect(lighten('#000000', 10)).toBe('#0a0a0a');
|
||||
expect(lighten('#000000', 20)).toBe('#141414');
|
||||
expect(lighten('#ffffff', 30)).toBe('#ffffff');
|
||||
});
|
||||
|
||||
test('lightens a color with alpha', () => {
|
||||
expect(lighten('#00000080', 10)).toBe('#0a0a0a80');
|
||||
expect(lighten('#00000080', 20)).toBe('#14141480');
|
||||
expect(lighten('#ffffff80', 30)).toBe('#ffffff80');
|
||||
});
|
||||
});
|
||||
|
||||
describe('darken', () => {
|
||||
test('darkens a color', () => {
|
||||
expect(darken('#ffffff', 10)).toBe('#f5f5f5');
|
||||
expect(darken('#ffffff', 20)).toBe('#ebebeb');
|
||||
expect(darken('#000000', 30)).toBe('#000000');
|
||||
});
|
||||
|
||||
test('darkens a color with alpha', () => {
|
||||
expect(darken('#ffffff80', 10)).toBe('#f5f5f580');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setOpacity', () => {
|
||||
test('sets the opacity of a color', () => {
|
||||
expect(setOpacity('#000000', 0.5)).toBe('#00000080');
|
||||
});
|
||||
|
||||
test('sets the opacity of a color with alpha', () => {
|
||||
expect(setOpacity('#00000000', 0.5)).toBe('#00000080');
|
||||
});
|
||||
});
|
||||
});
|
33
src/ui/color/color.models.ts
Normal file
33
src/ui/color/color.models.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
export { lighten, darken, setOpacity };
|
||||
|
||||
const clampHex = (value: number) => Math.max(0, Math.min(255, Math.round(value)));
|
||||
|
||||
function lighten(color: string, amount: number): string {
|
||||
const alpha = color.length === 9 ? color.slice(7) : '';
|
||||
const num = parseInt(color.slice(1, 7), 16);
|
||||
|
||||
const r = clampHex(((num >> 16) & 255) + amount);
|
||||
const g = clampHex(((num >> 8) & 255) + amount);
|
||||
const b = clampHex((num & 255) + amount);
|
||||
|
||||
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}${alpha}`;
|
||||
}
|
||||
|
||||
function darken(color: string, amount: number): string {
|
||||
return lighten(color, -amount);
|
||||
}
|
||||
|
||||
function setOpacity(color: string, opacity: number): string {
|
||||
const alpha = clampHex(Math.round(opacity * 255))
|
||||
.toString(16)
|
||||
.padStart(2, '0');
|
||||
|
||||
if (color.length === 7) {
|
||||
return `${color}${alpha}`;
|
||||
}
|
||||
|
||||
if (color.length === 9) {
|
||||
return `${color.slice(0, 7)}${alpha}`;
|
||||
}
|
||||
throw new Error('Invalid hex color');
|
||||
}
|
33
src/ui/demo/demo-wrapper.vue
Normal file
33
src/ui/demo/demo-wrapper.vue
Normal file
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<div mt-2 w-full p-8>
|
||||
<h1>c-lib components</h1>
|
||||
|
||||
<div flex>
|
||||
<div w-30 b-r b-gray b-op-10 b-r-solid pr-4>
|
||||
<c-button
|
||||
v-for="{ name } of demoRoutes"
|
||||
:key="name"
|
||||
variant="text"
|
||||
:to="{ name }"
|
||||
w-full
|
||||
important:justify-start
|
||||
:type="route.name === name ? 'primary' : 'default'"
|
||||
>
|
||||
{{ name }}
|
||||
</c-button>
|
||||
</div>
|
||||
|
||||
<div flex-1 pl-4>
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { demoRoutes } from './demo.routes';
|
||||
|
||||
const route = useRoute();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
25
src/ui/demo/demo.routes.ts
Normal file
25
src/ui/demo/demo.routes.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
const demoPages = import.meta.glob('../*/*.demo.vue');
|
||||
|
||||
export const demoRoutes = Object.keys(demoPages).map((path) => {
|
||||
const [, , fileName] = path.split('/');
|
||||
const name = fileName.split('.').shift();
|
||||
|
||||
console.log(path);
|
||||
|
||||
return {
|
||||
path: name,
|
||||
name,
|
||||
component: () => import(/* @vite-ignore */ path),
|
||||
} as RouteRecordRaw;
|
||||
});
|
||||
|
||||
export const routes = [
|
||||
{
|
||||
path: '/c-lib',
|
||||
name: 'c-lib',
|
||||
children: demoRoutes,
|
||||
component: () => import('./demo-wrapper.vue'),
|
||||
},
|
||||
];
|
|
@ -3,16 +3,20 @@ import { defineThemes } from './theme.models';
|
|||
export const { themes: appThemes, useTheme: useAppTheme } = defineThemes({
|
||||
light: {
|
||||
text: {
|
||||
baseColor: 'rgb(51, 54, 57)',
|
||||
mutedColor: 'rgba(118, 124, 130)',
|
||||
baseColor: '#333639',
|
||||
mutedColor: '#767c82',
|
||||
},
|
||||
default: {
|
||||
color: 'rgba(46, 51, 56, 0.05)',
|
||||
colorHover: 'rgba(46, 51, 56, 0.09)',
|
||||
colorPressed: 'rgba(46, 51, 56, 0.22)',
|
||||
},
|
||||
|
||||
primary: {
|
||||
color: '#18a058',
|
||||
colorHover: '#1ea54c',
|
||||
colorPressed: '#0C7A43',
|
||||
colorFaded: '#18a0582f',
|
||||
},
|
||||
|
||||
warning: {
|
||||
color: '#f59e0b',
|
||||
colorHover: '#f59e0b',
|
||||
|
@ -33,14 +37,19 @@ export const { themes: appThemes, useTheme: useAppTheme } = defineThemes({
|
|||
},
|
||||
dark: {
|
||||
text: {
|
||||
baseColor: 'rgba(255, 255, 255, 0.82)',
|
||||
mutedColor: 'rgba(255, 255, 255, 0.5)',
|
||||
baseColor: '#ffffffd1',
|
||||
mutedColor: '#ffffff80',
|
||||
},
|
||||
default: {
|
||||
color: 'rgba(255, 255, 255, 0.08)',
|
||||
colorHover: 'rgba(255, 255, 255, 0.12)',
|
||||
colorPressed: 'rgba(255, 255, 255, 0.24)',
|
||||
},
|
||||
|
||||
primary: {
|
||||
color: '#1ea54c',
|
||||
colorHover: '#36AD6A',
|
||||
colorPressed: '#0C7A43',
|
||||
colorFaded: '#18a0582f',
|
||||
},
|
||||
warning: {
|
||||
color: '#f59e0b',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue