style(projects): format code

This commit is contained in:
Soybean
2023-12-14 21:45:29 +08:00
parent a176dc443e
commit a748166399
127 changed files with 2472 additions and 3006 deletions

View File

@ -6,9 +6,7 @@
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
"*": ["./src/*"]
}
},
"dependencies": {

View File

@ -1,12 +1,13 @@
import { getColorPaletteFamily } from './palette';
import { getColorName } from './name';
import type { ColorPalette, ColorPaletteNumber, ColorPaletteItem, ColorPaletteFamily } from './type';
import type { ColorPalette, ColorPaletteFamily, ColorPaletteItem, ColorPaletteNumber } from './type';
import defaultPalettes from './json/palette.json';
/**
* get color palette by provided color and color name
* @param color the provided color
* @param colorName color name
* Get color palette by provided color and color name
*
* @param color The provided color
* @param colorName Color name
*/
export function getColorPalette(color: string, colorName: string) {
const colorPaletteFamily = getColorPaletteFamily(color, colorName);
@ -31,10 +32,11 @@ export function getColorPalette(color: string, colorName: string) {
}
/**
* get color by color palette number
* @param color color
* @param num color palette number
* @return color hexcode
* Get color by color palette number
*
* @param color Color
* @param num Color palette number
* @returns Color hexcode
*/
export function getColorByColorPaletteNumber(color: string, num: ColorPaletteNumber) {
const colorPalette = getColorPalette(color, color);
@ -46,9 +48,7 @@ export function getColorByColorPaletteNumber(color: string, num: ColorPaletteNum
export default getColorPalette;
/**
* the builtin color palettes
*/
/** The builtin color palettes */
const colorPalettes = defaultPalettes as ColorPaletteFamily[];
export { getColorName, colorPalettes };

View File

@ -1,4 +1,4 @@
import { getHex, getRgb, getHsl } from './color';
import { getHex, getHsl, getRgb } from './color';
import colorNames from './json/color-name.json';
export function getColorName(color: string) {

View File

@ -1,4 +1,4 @@
import { isValidColor, getHsl, getDeltaE, transformHslToHex } from './color';
import { getDeltaE, getHsl, isValidColor, transformHslToHex } from './color';
import { getColorName } from './name';
import type { ColorPaletteFamily, ColorPaletteFamilyWithNearestPalette } from './type';
import defaultPalettes from './json/palette.json';

View File

@ -1,35 +1,24 @@
/**
* the color palette number
*/
/** The color palette number */
export type ColorPaletteNumber = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950;
/**
* the color palette item
*/
/** The color palette item */
export type ColorPaletteItem = {
/**
* the color hexcode
*/
/** The color hexcode */
hexcode: string;
/**
* the color number
* The color number
*
* @link {@link ColorPaletteNumber}
*/
number: ColorPaletteNumber;
/**
* the color name
*/
/** The color name */
name: string;
};
export type ColorPaletteFamily = {
/**
* the color palette family key
*/
/** The color palette family key */
key: string;
/**
* the color palette family's palettes
*/
/** The color palette family's palettes */
palettes: ColorPaletteItem[];
};
@ -47,17 +36,14 @@ export type ColorPaletteFamilyWithNearestPalette = ColorPaletteFamily & {
};
export type ColorPalette = ColorPaletteFamily & {
/**
* the color map of the palette
*/
/** The color map of the palette */
colorMap: Map<ColorPaletteNumber, ColorPaletteItem>;
/**
* the main color of the palette
* @description which number is 500
* The main color of the palette
*
* Which number is 500
*/
main: ColorPaletteItemWithName;
/**
* the match color of the palette
*/
/** The match color of the palette */
match: ColorPaletteItemWithName;
};

View File

@ -1,3 +1,4 @@
import process from 'node:process';
import path from 'node:path';
import { defineConfig } from 'vitepress';

View File

@ -45,11 +45,7 @@
var(--vp-c-brand-darker)
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
var(--vp-c-brand-lightest) 30%,
var(--vp-c-brand) 50%
);
--vp-home-hero-image-background-image: linear-gradient(-45deg, var(--vp-c-brand-lightest) 30%, var(--vp-c-brand) 50%);
--vp-home-hero-image-filter: blur(40px);
}

View File

@ -2,11 +2,11 @@
"name": "@sa/docs",
"version": "1.0.0",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"dev": "vitepress dev",
"serve": "vitepress serve"
},
"devDependencies": {
"vitepress": "1.0.0-rc.27"
"vitepress": "1.0.0-rc.31"
}
}

View File

@ -6,9 +6,7 @@
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
"*": ["./src/*"]
}
}
}

View File

@ -1,8 +1,9 @@
import { ref } from 'vue';
/**
* boolean
* @param initValue init value
* Boolean
*
* @param initValue Init value
*/
export default function useBoolean(initValue = false) {
const bool = ref(initValue);

View File

@ -2,61 +2,60 @@ import { inject, provide } from 'vue';
import type { InjectionKey } from 'vue';
/**
* use context
* @param contextName context name
* @param fn context function
* Use context
*
* @example
* ```ts
* // there are three vue files: A.vue, B.vue, C.vue, and A.vue is the parent component of B.vue and C.vue
* ```ts
* // there are three vue files: A.vue, B.vue, C.vue, and A.vue is the parent component of B.vue and C.vue
*
* // context.ts
* import { ref } from 'vue';
* import { useContext } from '@sa/hooks';
* // context.ts
* import { ref } from 'vue';
* import { useContext } from '@sa/hooks';
*
* export const { setupStore, useStore } = useContext('demo', () => {
* const count = ref(0);
* export const { setupStore, useStore } = useContext('demo', () => {
* const count = ref(0);
*
* function increment() {
* count.value++;
* }
* function increment() {
* count.value++;
* }
*
* function decrement() {
* count.value--;
* }
* function decrement() {
* count.value--;
* }
*
* return {
* count,
* increment,
* decrement
* };
* })
* ```
* return {
* count,
* increment,
* decrement
* };
* })
* ``` // A.vue
* ```vue
* <template>
* <div>A</div>
* </template>
* <script setup lang="ts">
* import { setupStore } from './context';
*
* // A.vue
* ```vue
* <template>
* <div>A</div>
* </template>
* <script setup lang="ts">
* import { setupStore } from './context';
* setupStore();
* // const { increment } = setupStore(); // also can control the store in the parent component
* </script>
* ``` // B.vue
* ```vue
* <template>
* <div>B</div>
* </template>
* <script setup lang="ts">
* import { useStore } from './context';
*
* setupStore();
* // const { increment } = setupStore(); // also can control the store in the parent component
* </script>
* ```
* // B.vue
* ```vue
* <template>
* <div>B</div>
* </template>
* <script setup lang="ts">
* import { useStore } from './context';
* const { count, increment } = useStore();
* </script>
* ```;
*
* const { count, increment } = useStore();
* </script>
* ```
* // C.vue is same as B.vue
*
* // C.vue is same as B.vue
* @param contextName Context name
* @param fn Context function
*/
export default function useContext<T extends (...args: any[]) => any>(contextName: string, fn: T) {
type Context = ReturnType<T>;
@ -69,20 +68,14 @@ export default function useContext<T extends (...args: any[]) => any>(contextNam
}
return {
/**
* setup store in the parent component
*/
/** Setup store in the parent component */
setupStore,
/**
* use store in the child component
*/
/** Use store in the child component */
useStore
};
}
/**
* create context
*/
/** Create context */
function createContext<T>(contextName: string) {
const injectKey: InjectionKey<T> = Symbol(contextName);

View File

@ -1,8 +1,9 @@
import useBoolean from './use-boolean';
/**
* loading
* @param initValue init value
* Loading
*
* @param initValue Init value
*/
export default function useLoading(initValue = false) {
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue);

View File

@ -2,33 +2,27 @@ import { h } from 'vue';
import type { Component } from 'vue';
/**
* svg icon render hook
* @param SvgIcon svg icon component
* Svg icon render hook
*
* @param SvgIcon Svg icon component
*/
export default function useSvgIconRender(SvgIcon: Component) {
interface IconConfig {
/**
* iconify icon name
*/
/** Iconify icon name */
icon?: string;
/**
* local icon name
*/
/** Local icon name */
localIcon?: string;
/**
* icon color
*/
/** Icon color */
color?: string;
/**
* icon size
*/
/** Icon size */
fontSize?: number;
}
type IconStyle = Partial<Pick<CSSStyleDeclaration, 'color' | 'fontSize'>>;
/**
* svg icon VNode
* Svg icon VNode
*
* @param config
*/
const SvgIconVNode = (config: IconConfig) => {

View File

@ -1,19 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"types": ["node"]
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
"*": ["./src/*"]
}
},
"dependencies": {

View File

@ -1,4 +1,4 @@
import AdminLayout, { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX } from './libs/admin-layout';
import AdminLayout, { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './libs/admin-layout';
import PageTab from './libs/page-tab';
import SimpleScrollbar from './libs/simple-scrollbar';
import ColorPicker from './libs/color-picker';

View File

@ -14,4 +14,5 @@ declare const styles: {
readonly 'sider-padding-top': string;
readonly 'sider-padding-bottom': string;
};
export = styles;
export default styles;

View File

@ -1,5 +1,5 @@
import AdminLayout from './index.vue';
import { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX } from './shared';
import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './shared';
export default AdminLayout;
export { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX };

View File

@ -1,129 +1,7 @@
<template>
<div :class="['relative h-full', commonClass]" :style="cssVars">
<div
:id="isWrapperScroll ? scrollElId : undefined"
:class="['flex flex-col h-full', commonClass, scrollWrapperClass, { 'overflow-y-auto': isWrapperScroll }]"
>
<!-- Header -->
<template v-if="showHeader">
<header
v-show="!fullContent"
:class="[
style['layout-header'],
'flex-shrink-0',
commonClass,
headerClass,
headerLeftGapClass,
{ 'absolute top-0 left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="header"></slot>
</header>
<div
v-show="!fullContent && fixedHeaderAndTab"
:class="[style['layout-header-placement'], 'flex-shrink-0 overflow-hidden']"
></div>
</template>
<!-- Tab -->
<template v-if="showTab">
<div
:class="[
style['layout-tab'],
'flex-shrink-0',
commonClass,
tabClass,
{ 'top-0!': fullContent || !showHeader },
leftGapClass,
{ 'absolute left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="tab"></slot>
</div>
<div
v-show="fullContent || fixedHeaderAndTab"
:class="[style['layout-tab-placement'], 'flex-shrink-0 overflow-hidden']"
></div>
</template>
<!-- Sider -->
<template v-if="showSider">
<aside
v-show="!fullContent"
:class="[
'absolute left-0 top-0 h-full',
commonClass,
siderClass,
siderPaddingClass,
siderCollapse ? style['layout-sider_collapsed'] : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
</template>
<!-- Mobile Sider -->
<template v-if="showMobileSider">
<aside
:class="[
'absolute left-0 top-0 w-0 h-full bg-white',
commonClass,
mobileSiderClass,
style['layout-mobile-sider'],
siderCollapse ? 'overflow-hidden' : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
<div
v-show="!siderCollapse"
:class="['absolute left-0 top-0 w-full h-full bg-[rgba(0,0,0,0.2)]', style['layout-mobile-sider-mask']]"
@click="handleClickMask"
></div>
</template>
<!-- Main Content -->
<main
:id="isContentScroll ? scrollElId : undefined"
:class="[
'flex flex-col flex-grow',
commonClass,
contentClass,
leftGapClass,
{ 'overflow-y-auto': isContentScroll }
]"
>
<slot></slot>
</main>
<!-- Footer -->
<template v-if="showFooter">
<footer
v-show="!fullContent"
:class="[
style['layout-footer'],
'flex-shrink-0',
commonClass,
footerClass,
footerLeftGapClass,
{ 'absolute left-0 bottom-0 w-full': fixedFooter }
]"
>
<slot name="footer"></slot>
</footer>
<div
v-show="!fullContent && fixedFooter"
:class="[style['layout-footer-placement'], 'flex-shrink-0 overflow-hidden']"
></div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { AdminLayoutProps } from '../../types';
import { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX, createLayoutCssVars } from './shared';
import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID, createLayoutCssVars } from './shared';
import style from './index.module.css';
defineOptions({
@ -150,31 +28,29 @@ const props = withDefaults(defineProps<AdminLayoutProps>(), {
rightFooter: false
});
const emit = defineEmits<Emits>();
const slots = defineSlots<Slots>();
interface Emits {
/**
* update siderCollapse
*/
/** Update siderCollapse */
(e: 'update:siderCollapse', collapse: boolean): void;
}
const emit = defineEmits<Emits>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/** main */
/** Main */
default?: SlotFn;
/** header */
/** Header */
header?: SlotFn;
/** tab */
/** Tab */
tab?: SlotFn;
/** sider */
/** Sider */
sider?: SlotFn;
/** footer */
/** Footer */
footer?: SlotFn;
};
const slots = defineSlots<Slots>();
const cssVars = computed(() => createLayoutCssVars(props));
// config visible
@ -235,4 +111,126 @@ function handleClickMask() {
}
</script>
<template>
<div class="relative h-full" :class="[commonClass]" :style="cssVars">
<div
:id="isWrapperScroll ? scrollElId : undefined"
class="flex flex-col h-full"
:class="[commonClass, scrollWrapperClass, { 'overflow-y-auto': isWrapperScroll }]"
>
<!-- Header -->
<template v-if="showHeader">
<header
v-show="!fullContent"
class="flex-shrink-0"
:class="[
style['layout-header'],
commonClass,
headerClass,
headerLeftGapClass,
{ 'absolute top-0 left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="header"></slot>
</header>
<div
v-show="!fullContent && fixedHeaderAndTab"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-header-placement']]"
></div>
</template>
<!-- Tab -->
<template v-if="showTab">
<div
class="flex-shrink-0"
:class="[
style['layout-tab'],
commonClass,
tabClass,
{ 'top-0!': fullContent || !showHeader },
leftGapClass,
{ 'absolute left-0 w-full': fixedHeaderAndTab }
]"
>
<slot name="tab"></slot>
</div>
<div
v-show="fullContent || fixedHeaderAndTab"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-tab-placement']]"
></div>
</template>
<!-- Sider -->
<template v-if="showSider">
<aside
v-show="!fullContent"
class="absolute left-0 top-0 h-full"
:class="[
commonClass,
siderClass,
siderPaddingClass,
siderCollapse ? style['layout-sider_collapsed'] : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
</template>
<!-- Mobile Sider -->
<template v-if="showMobileSider">
<aside
class="absolute left-0 top-0 w-0 h-full bg-white"
:class="[
commonClass,
mobileSiderClass,
style['layout-mobile-sider'],
siderCollapse ? 'overflow-hidden' : style['layout-sider']
]"
>
<slot name="sider"></slot>
</aside>
<div
v-show="!siderCollapse"
class="absolute left-0 top-0 w-full h-full bg-[rgba(0,0,0,0.2)]"
:class="[style['layout-mobile-sider-mask']]"
@click="handleClickMask"
></div>
</template>
<!-- Main Content -->
<main
:id="isContentScroll ? scrollElId : undefined"
class="flex flex-col flex-grow"
:class="[commonClass, contentClass, leftGapClass, { 'overflow-y-auto': isContentScroll }]"
>
<slot></slot>
</main>
<!-- Footer -->
<template v-if="showFooter">
<footer
v-show="!fullContent"
class="flex-shrink-0"
:class="[
style['layout-footer'],
commonClass,
footerClass,
footerLeftGapClass,
{ 'absolute left-0 bottom-0 w-full': fixedFooter }
]"
>
<slot name="footer"></slot>
</footer>
<div
v-show="!fullContent && fixedFooter"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-footer-placement']]"
></div>
</template>
</div>
</div>
</template>
<style scoped></style>

View File

@ -1,18 +1,15 @@
import type { AdminLayoutProps, LayoutCssVarsProps, LayoutCssVars } from '../../types';
import type { AdminLayoutProps, LayoutCssVars, LayoutCssVarsProps } from '../../types';
/**
* the id of the scroll element of the layout
*/
/** The id of the scroll element of the layout */
export const LAYOUT_SCROLL_EL_ID = '__SCROLL_EL_ID__';
/**
* the max z-index of the layout
*/
/** The max z-index of the layout */
export const LAYOUT_MAX_Z_INDEX = 100;
/**
* create layout css vars by css vars props
* @param props css vars props
* Create layout css vars by css vars props
*
* @param props Css vars props
*/
function createLayoutCssVarsByCssVarsProps(props: LayoutCssVarsProps) {
const cssVars: LayoutCssVars = {
@ -32,7 +29,8 @@ function createLayoutCssVarsByCssVarsProps(props: LayoutCssVarsProps) {
}
/**
* create layout css vars
* Create layout css vars
*
* @param props
*/
export function createLayoutCssVars(props: AdminLayoutProps) {

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue';
import { onMounted, ref, watch } from 'vue';
import ColorPicker from '@simonwep/pickr';
import '@simonwep/pickr/dist/themes/nano.min.css';
@ -7,12 +7,6 @@ defineOptions({
name: 'ColorPicker'
});
interface Props {
color: string;
palettes?: string[];
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
palettes: () => [
'#3b82f6',
@ -35,12 +29,18 @@ const props = withDefaults(defineProps<Props>(), {
]
});
const emit = defineEmits<Emits>();
interface Props {
color: string;
palettes?: string[];
disabled?: boolean;
}
interface Emits {
(e: 'update:color', value: string): void;
}
const emit = defineEmits<Emits>();
const domRef = ref<HTMLElement | null>(null);
const instance = ref<ColorPicker | null>(null);

View File

@ -1,7 +1,43 @@
<script setup lang="ts">
import type { PageTabProps } from '../../types';
import style from './index.module.css';
defineOptions({
name: 'ButtonTab'
});
defineProps<PageTabProps>();
defineSlots<Slots>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/**
* Slot
*
* The center content of the tab
*/
default?: SlotFn;
/**
* Slot
*
* The left content of the tab
*/
prefix?: SlotFn;
/**
* Slot
*
* The right content of the tab
*/
suffix?: SlotFn;
};
</script>
<template>
<div
class=":soy: relative inline-flex justify-center items-center gap-12px px-12px py-4px border-1px border-solid rounded-4px cursor-pointer whitespace-nowrap"
:class="[
':soy: relative inline-flex justify-center items-center gap-12px px-12px py-4px border-1px border-solid rounded-4px cursor-pointer whitespace-nowrap',
style['button-tab'],
{ [style['button-tab_dark']]: darkMode },
{ [style['button-tab_active']]: active },
@ -14,36 +50,4 @@
</div>
</template>
<script setup lang="ts">
import style from './index.module.css';
import type { PageTabProps } from '../../types';
defineOptions({
name: 'ButtonTab'
});
defineProps<PageTabProps>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/**
* slot
* @description the center content of the tab
*/
default?: SlotFn;
/**
* slot
* @description the left content of the tab
*/
prefix?: SlotFn;
/**
* slot
* @description the right content of the tab
*/
suffix?: SlotFn;
};
defineSlots<Slots>();
</script>
<style scoped></style>

View File

@ -1,5 +1,11 @@
<script setup lang="ts">
defineOptions({
name: 'ChromeTabBg'
});
</script>
<template>
<svg style="width: 100%; height: 100%">
<svg class="wh-full">
<defs>
<symbol id="geometry-left" viewBox="0 0 214 36">
<path d="M17 0h197v36H0v-2c4.5 0 9-3.5 9-8V8c0-4.5 3.5-8 8-8z"></path>
@ -22,10 +28,4 @@
</svg>
</template>
<script setup lang="ts">
defineOptions({
name: 'ChromeTabBg'
});
</script>
<style scoped></style>

View File

@ -1,27 +1,7 @@
<template>
<div
:class="[
':soy: relative inline-flex justify-center items-center gap-16px -mr-18px px-24px py-6px cursor-pointer whitespace-nowrap',
style['chrome-tab'],
{ [style['chrome-tab_dark']]: darkMode },
{ [style['chrome-tab_active']]: active },
{ [style['chrome-tab_active_dark']]: active && darkMode }
]"
>
<div :class="[':soy: absolute left-0 top-0 -z-1 w-full h-full pointer-events-none', style['chrome-tab__bg']]">
<ChromeTabBg />
</div>
<slot name="prefix"></slot>
<slot></slot>
<slot name="suffix"></slot>
<div :class="[':soy: absolute right-7px w-1px h-16px bg-#1f2225', style['chrome-tab-divider']]"></div>
</div>
</template>
<script setup lang="ts">
import type { PageTabProps } from '../../types';
import ChromeTabBg from './chrome-tab-bg.vue';
import style from './index.module.css';
import type { PageTabProps } from '../../types';
defineOptions({
name: 'ChromeTab'
@ -29,27 +9,50 @@ defineOptions({
defineProps<PageTabProps>();
defineSlots<Slots>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/**
* slot
* @description the center content of the tab
* Slot
*
* The center content of the tab
*/
default?: SlotFn;
/**
* slot
* @description the left content of the tab
* Slot
*
* The left content of the tab
*/
prefix?: SlotFn;
/**
* slot
* @description the right content of the tab
* Slot
*
* The right content of the tab
*/
suffix?: SlotFn;
};
defineSlots<Slots>();
</script>
<template>
<div
class=":soy: relative inline-flex justify-center items-center gap-16px -mr-18px px-24px py-6px cursor-pointer whitespace-nowrap"
:class="[
style['chrome-tab'],
{ [style['chrome-tab_dark']]: darkMode },
{ [style['chrome-tab_active']]: active },
{ [style['chrome-tab_active_dark']]: active && darkMode }
]"
>
<div class=":soy: absolute left-0 top-0 -z-1 w-full h-full pointer-events-none" :class="[style['chrome-tab__bg']]">
<ChromeTabBg />
</div>
<slot name="prefix"></slot>
<slot></slot>
<slot name="suffix"></slot>
<div class=":soy: absolute right-7px w-1px h-16px bg-#1f2225" :class="[style['chrome-tab-divider']]"></div>
</div>
</template>
<style scoped></style>

View File

@ -1,3 +1,19 @@
<script setup lang="ts">
defineOptions({
name: 'IconClose'
});
const emit = defineEmits<Emits>();
interface Emits {
(e: 'click'): void;
}
function handleClick() {
emit('click');
}
</script>
<template>
<div
class=":soy: relative inline-flex justify-center items-center w-16px h-16px text-14px rd-50%"
@ -12,20 +28,4 @@
</div>
</template>
<script setup lang="ts">
defineOptions({
name: 'IconClose'
});
interface Emits {
(e: 'click'): void;
}
const emit = defineEmits<Emits>();
function handleClick() {
emit('click');
}
</script>
<style scoped></style>

View File

@ -11,4 +11,5 @@ declare const styles: {
readonly 'chrome-tab_dark': string;
readonly 'chrome-tab-divider': string;
};
export = styles;
export default styles;

View File

@ -1,26 +1,12 @@
<template>
<component :is="activeTabComponent.component" :class="activeTabComponent.class" :style="cssVars" v-bind="bindProps">
<template #prefix>
<slot name="prefix"></slot>
</template>
<slot></slot>
<template #suffix>
<slot name="suffix">
<SvgIconClose v-if="closable" :class="[style['icon_close']]" @click="handleClose" />
</slot>
</template>
</component>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { Component } from 'vue';
import { createTabCssVars, ACTIVE_COLOR } from './shared';
import type { PageTabMode, PageTabProps } from '../../types';
import { ACTIVE_COLOR, createTabCssVars } from './shared';
import ChromeTab from './chrome-tab.vue';
import ButtonTab from './button-tab.vue';
import SvgIconClose from './icon-close.vue';
import style from './index.module.css';
import type { PageTabProps, PageTabMode } from '../../types';
defineOptions({
name: 'PageTab'
@ -33,34 +19,37 @@ const props = withDefaults(defineProps<PageTabProps>(), {
closable: true
});
const emit = defineEmits<Emits>();
defineSlots<Slots>();
interface Emits {
(e: 'close'): void;
}
const emit = defineEmits<Emits>();
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/**
* slot
* @description the center content of the tab
* Slot
*
* The center content of the tab
*/
default?: SlotFn;
/**
* slot
* @description the left content of the tab
* Slot
*
* The left content of the tab
*/
prefix?: SlotFn;
/**
* slot
* @description the right content of the tab
* Slot
*
* The right content of the tab
*/
suffix?: SlotFn;
};
defineSlots<Slots>();
const activeTabComponent = computed(() => {
const { mode, chromeClass, buttonClass } = props;
@ -91,4 +80,18 @@ function handleClose() {
}
</script>
<template>
<component :is="activeTabComponent.component" :class="activeTabComponent.class" :style="cssVars" v-bind="bindProps">
<template #prefix>
<slot name="prefix"></slot>
</template>
<slot></slot>
<template #suffix>
<slot name="suffix">
<SvgIconClose v-if="closable" :class="[style['icon_close']]" @click="handleClose" />
</slot>
</template>
</component>
</template>
<style scoped></style>

View File

@ -1,9 +1,7 @@
import { addColorAlpha, transformColorWithOpacity } from '@sa/utils';
import type { PageTabCssVarsProps, PageTabCssVars } from '../../types';
import type { PageTabCssVars, PageTabCssVarsProps } from '../../types';
/**
* the active color of the tab
*/
/** The active color of the tab */
export const ACTIVE_COLOR = '#1890ff';
function createCssVars(props: PageTabCssVarsProps) {

View File

@ -1,146 +1,156 @@
/**
* header config
*/
/** Header config */
interface AdminLayoutHeaderConfig {
/**
* whether header is visible
* Whether header is visible
*
* @default true
*/
headerVisible?: boolean;
/**
* header class
* Header class
*
* @default ''
*/
headerClass?: string;
/**
* header height
* Header height
*
* @default 56px
*/
headerHeight?: number;
}
/**
* tab config
*/
/** Tab config */
interface AdminLayoutTabConfig {
/**
* whether tab is visible
* Whether tab is visible
*
* @default true
*/
tabVisible?: boolean;
/**
* tab class
* Tab class
*
* @default ''
*/
tabClass?: string;
/**
* tab height
* Tab height
*
* @default 48px
*/
tabHeight?: number;
}
/**
* sider config
*/
/** Sider config */
interface AdminLayoutSiderConfig {
/**
* whether sider is visible
* Whether sider is visible
*
* @default true
*/
siderVisible?: boolean;
/**
* sider class
* Sider class
*
* @default ''
*/
siderClass?: string;
/**
* mobile sider class
* Mobile sider class
*
* @default ''
*/
mobileSiderClass?: string;
/**
* sider collapse status
* Sider collapse status
*
* @default false
*/
siderCollapse?: boolean;
/**
* sider width when collapse is false
* Sider width when collapse is false
*
* @default '220px'
*/
siderWidth?: number;
/**
* sider width when collapse is true
* Sider width when collapse is true
*
* @default '64px'
*/
siderCollapsedWidth?: number;
}
/**
* content config
*/
/** Content config */
export interface AdminLayoutContentConfig {
/**
* content class
* Content class
*
* @default ''
*/
contentClass?: string;
/**
* whether content is full the page
* @description if true, other elements will be hidden by `display: none`
* Whether content is full the page
*
* If true, other elements will be hidden by `display: none`
*/
fullContent?: boolean;
}
/**
* footer config
*/
/** Footer config */
export interface AdminLayoutFooterConfig {
/**
* whether footer is visible
* Whether footer is visible
*
* @default true
*/
footerVisible?: boolean;
/**
* whether footer is fixed
* Whether footer is fixed
*
* @default true
*/
fixedFooter?: boolean;
/**
* footer class
* Footer class
*
* @default ''
*/
footerClass?: string;
/**
* footer height
* Footer height
*
* @default 48px
*/
footerHeight?: number;
/**
* whether footer is on the right side
* @description when the layout is vertical, the footer is on the right side
* Whether footer is on the right side
*
* When the layout is vertical, the footer is on the right side
*/
rightFooter?: boolean;
}
/**
* layout mode
* - horizontal
* - vertical
* Layout mode
*
* - Horizontal
* - Vertical
*/
export type LayoutMode = 'horizontal' | 'vertical';
/**
* the scroll mode when content overflow
* - wrapper: the layout component's wrapper element has a scrollbar
* - content: the layout component's content element has a scrollbar
* The scroll mode when content overflow
*
* - Wrapper: the layout component's wrapper element has a scrollbar
* - Content: the layout component's content element has a scrollbar
*
* @default 'wrapper'
*/
export type LayoutScrollMode = 'wrapper' | 'content';
/**
* admin layout props
*/
/** Admin layout props */
export interface AdminLayoutProps
extends AdminLayoutHeaderConfig,
AdminLayoutTabConfig,
@ -148,52 +158,58 @@ export interface AdminLayoutProps
AdminLayoutContentConfig,
AdminLayoutFooterConfig {
/**
* layout mode
* Layout mode
*
* - {@link LayoutMode}
*/
mode?: LayoutMode;
/** is mobile layout */
/** Is mobile layout */
isMobile?: boolean;
/**
* scroll mode
* Scroll mode
*
* - {@link ScrollMode}
*/
scrollMode?: LayoutScrollMode;
/**
* the id of the scroll element of the layout
* @description it can be used to get the corresponding Dom and scroll it
* The id of the scroll element of the layout
*
* It can be used to get the corresponding Dom and scroll it
*
* @example
* use the default id by import
* ```ts
* import { adminLayoutScrollElId } from '@sa/vue-materials';
* ```
*
* @default
* ```ts
* const adminLayoutScrollElId = '__ADMIN_LAYOUT_SCROLL_EL_ID__'
* ```
* @example use the default id by import
* ```ts
* import { adminLayoutScrollElId } from '@sa/vue-materials';
* ```
*/
scrollElId?: string;
/**
* the class of the scroll element
*/
/** The class of the scroll element */
scrollElClass?: string;
/**
* the class of the scroll wrapper element
*/
/** The class of the scroll wrapper element */
scrollWrapperClass?: string;
/**
* the common class of the layout
* @description is can be used to configure the transition animation
* The common class of the layout
*
* Is can be used to configure the transition animation
*
* @default 'transition-all-300'
*/
commonClass?: string;
/**
* whether fix the header and tab
* Whether fix the header and tab
*
* @default true
*/
fixedTop?: boolean;
/**
* the max z-index of the layout
* @description the z-index of Header,Tab,Sider and Footer will not exceed this value
* The max z-index of the layout
*
* The z-index of Header,Tab,Sider and Footer will not exceed this value
*/
maxZIndex?: number;
}
@ -222,48 +238,44 @@ export type LayoutCssVars = {
};
/**
* the mode of the tab
* - button: button style
* - chrome: chrome style
* The mode of the tab
*
* - Button: button style
* - Chrome: chrome style
*
* @default chrome
*/
export type PageTabMode = 'button' | 'chrome';
export interface PageTabProps {
/**
* whether is dark mode
*/
/** Whether is dark mode */
darkMode?: boolean;
/**
* the mode of the tab
* The mode of the tab
*
* - {@link TabMode}
*/
mode?: PageTabMode;
/**
* the common class of the layout
* @description is can be used to configure the transition animation
* The common class of the layout
*
* Is can be used to configure the transition animation
*
* @default 'transition-all-300'
*/
commonClass?: string;
/**
* the class of the button tab
*/
/** The class of the button tab */
buttonClass?: string;
/**
* the class of the chrome tab
*/
/** The class of the chrome tab */
chromeClass?: string;
/**
* whether the tab is active
*/
/** Whether the tab is active */
active?: boolean;
/**
* the color of the active tab
*/
/** The color of the active tab */
activeColor?: string;
/**
* whether the tab is closable
* @description show the close icon when true
* Whether the tab is closable
*
* Show the close icon when true
*/
closable?: boolean;
}

View File

@ -1,19 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"types": ["node"]
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
"*": ["./src/*"]
}
},
"dependencies": {

View File

@ -1,19 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"types": ["node"]
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]

View File

@ -1,6 +1,6 @@
#!/usr/bin/env node
const path = require('path');
const path = require('node:path');
const jiti = require('jiti')(__filename);
jiti(path.resolve(__dirname, './src/index.ts'));

View File

@ -1,19 +1,17 @@
{
"name": "@sa/scripts",
"version": "1.0.0",
"bin": {
"sa": "./bin.cjs"
},
"exports": {
".": "./src/index.ts"
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
"*": ["./src/*"]
}
},
"bin": {
"sa": "./bin.cjs"
},
"devDependencies": {
"c12": "1.5.1",
"cac": "6.7.14",
@ -21,8 +19,8 @@
"enquirer": "2.4.1",
"execa": "8.0.1",
"jiti": "1.21.0",
"lint-staged": "15.1.0",
"npm-check-updates": "16.14.6",
"lint-staged": "15.2.0",
"npm-check-updates": "16.14.12",
"rimraf": "5.0.5"
}
}

View File

@ -1,7 +1,7 @@
import path from 'node:path';
import { readFileSync } from 'node:fs';
import enquirer from 'enquirer';
import { bgRed, red, green } from 'kolorist';
import { bgRed, green, red } from 'kolorist';
import { execCommand } from '../shared';
import type { CliOption } from '../types';

View File

@ -1,3 +1,4 @@
import process from 'node:process';
import { loadConfig } from 'c12';
import type { CliOption } from '../types';

View File

@ -1,7 +1,7 @@
import cac from 'cac';
import { blue, lightGreen } from 'kolorist';
import { version } from '../package.json';
import { cleanup, updatePkg, gitCommit, gitCommitVerify, prettierWrite, execLintStaged } from './commands';
import { cleanup, execLintStaged, gitCommit, gitCommitVerify, prettierWrite, updatePkg } from './commands';
import { loadCliOptions } from './config';
type Command = 'cleanup' | 'update-pkg' | 'git-commit' | 'git-commit-verify' | 'prettier-write' | 'lint-staged';

View File

@ -1,37 +1,33 @@
export interface CliOption {
/**
* the project root directory
*/
/** The project root directory */
cwd: string;
/**
* cleanup dirs
* Cleanup dirs
*
* Glob pattern syntax {@link https://github.com/isaacs/minimatch}
*
* @default
* ```json
* ["** /dist", "** /pnpm-lock.yaml", "** /node_modules", "!node_modules/**"]
* ```
* @description glob pattern syntax {@link https://github.com/isaacs/minimatch}
*/
cleanupDirs: string[];
/**
* git commit types
*/
/** Git commit types */
gitCommitTypes: [string, string][];
/**
* git commit scopes
*/
/** Git commit scopes */
gitCommitScopes: [string, string][];
/**
* npm-check-updates command args
* @default ["--deep","-u"]
* Npm-check-updates command args
*
* @default ['--deep', '-u']
*/
ncuCommandArgs: string[];
/**
* prettier write glob
* @description glob pattern syntax {@link https://github.com/micromatch/micromatch}
* Prettier write glob
*
* Glob pattern syntax {@link https://github.com/micromatch/micromatch}
*/
prettierWriteGlob: string[];
/**
* lint-staged config
*/
/** Lint-staged config */
lintStagedConfig: Record<string, string | string[]>;
}

View File

@ -1,19 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"types": ["node"]
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*", "typings/**/*"],
"exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
"*": ["./src/*"]
}
}
}

View File

@ -1,19 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"types": ["node"]
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]

View File

@ -6,9 +6,7 @@
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
"*": ["./src/*"]
}
},
"dependencies": {

View File

@ -6,29 +6,32 @@ import type { AnyColor, HsvColor, RgbColor } from 'colord';
extend([namesPlugin, mixPlugin]);
/**
* add color alpha
* @param color - color
* @param alpha - alpha (0 - 1)
* Add color alpha
*
* @param color - Color
* @param alpha - Alpha (0 - 1)
*/
export function addColorAlpha(color: string, alpha: number) {
return colord(color).alpha(alpha).toHex();
}
/**
* mix color
* @param firstColor - first color
* @param secondColor - second color
* @param ratio - the ratio of the second color (0 - 1)
* Mix color
*
* @param firstColor - First color
* @param secondColor - Second color
* @param ratio - The ratio of the second color (0 - 1)
*/
export function mixColor(firstColor: string, secondColor: string, ratio: number) {
return colord(firstColor).mix(secondColor, ratio).toHex();
}
/**
* transform color with opacity to similar color without opacity
* @param color - color
* @param alpha - alpha (0 - 1)
* @param bgColor background color (usually white or black)
* Transform color with opacity to similar color without opacity
*
* @param color - Color
* @param alpha - Alpha (0 - 1)
* @param bgColor Background color (usually white or black)
*/
export function transformColorWithOpacity(color: string, alpha: number, bgColor = '#ffffff') {
const originColor = addColorAlpha(color, alpha);
@ -50,67 +53,57 @@ export function transformColorWithOpacity(color: string, alpha: number, bgColor
}
/**
* is white color
* @param color - color
* Is white color
*
* @param color - Color
*/
export function isWhiteColor(color: string) {
return colord(color).isEqual('#ffffff');
}
/**
* get rgb of color
* @param color color
* Get rgb of color
*
* @param color Color
*/
export function getRgbOfColor(color: string) {
return colord(color).toRgb();
}
/**
* hue step
*/
/** Hue step */
const hueStep = 2;
/**
* saturation step, light color part
*/
/** Saturation step, light color part */
const saturationStep = 16;
/**
* saturation step, dark color part
*/
/** Saturation step, dark color part */
const saturationStep2 = 5;
/**
* brightness step, light color part
*/
/** Brightness step, light color part */
const brightnessStep1 = 5;
/**
* brightness step, dark color part
*/
/** Brightness step, dark color part */
const brightnessStep2 = 15;
/**
* light color count, main color up
*/
/** Light color count, main color up */
const lightColorCount = 5;
/**
* dark color count, main color down
*/
/** Dark color count, main color down */
const darkColorCount = 4;
/**
* the color index of color palette
* @description from left to right, the color is from light to dark, 6 is main color
* The color index of color palette
*
* From left to right, the color is from light to dark, 6 is main color
*/
type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
/**
* get color palette (from left to right, the color is from light to dark, 6 is main color)
* @param color - color
* @param index - the color index of color palette (the main color index is 6)
* @returns hex color
* Get color palette (from left to right, the color is from light to dark, 6 is main color)
*
* @param color - Color
* @param index - The color index of color palette (the main color index is 6)
* @returns Hex color
*/
export function getColorPalette(color: AnyColor, index: ColorIndex): string {
const transformColor = colord(color);
if (!transformColor.isValid()) {
throw Error('invalid input color value');
throw new Error('invalid input color value');
}
if (index === 6) {
@ -130,9 +123,7 @@ export function getColorPalette(color: AnyColor, index: ColorIndex): string {
return colord(newHsv).toHex();
}
/**
* map of dark color index and opacity
*/
/** Map of dark color index and opacity */
const darkColorMap = [
{ index: 7, opacity: 0.15 },
{ index: 6, opacity: 0.25 },
@ -147,10 +138,11 @@ const darkColorMap = [
];
/**
* get color palettes
* @param color - color
* @param darkTheme - dark theme
* @param darkThemeMixColor - dark theme mix color (default: #141414)
* Get color palettes
*
* @param color - Color
* @param darkTheme - Dark theme
* @param darkThemeMixColor - Dark theme mix color (default: #141414)
*/
export function getColorPalettes(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] {
const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
@ -171,10 +163,11 @@ export function getColorPalettes(color: AnyColor, darkTheme = false, darkThemeMi
}
/**
* get hue
* @param hsv - hsv format color
* @param i - the relative distance from 6
* @param isLight - is light color
* Get hue
*
* @param hsv - Hsv format color
* @param i - The relative distance from 6
* @param isLight - Is light color
*/
function getHue(hsv: HsvColor, i: number, isLight: boolean) {
let hue: number;
@ -199,10 +192,11 @@ function getHue(hsv: HsvColor, i: number, isLight: boolean) {
}
/**
* get saturation
* @param hsv - hsv format color
* @param i - the relative distance from 6
* @param isLight - is light color
* Get saturation
*
* @param hsv - Hsv format color
* @param i - The relative distance from 6
* @param isLight - Is light color
*/
function getSaturation(hsv: HsvColor, i: number, isLight: boolean) {
if (hsv.h === 0 && hsv.s === 0) {
@ -235,10 +229,11 @@ function getSaturation(hsv: HsvColor, i: number, isLight: boolean) {
}
/**
* get value of hsv
* @param hsv - hsv format color
* @param i - the relative distance from 6
* @param isLight - is light color
* Get value of hsv
*
* @param hsv - Hsv format color
* @param i - The relative distance from 6
* @param isLight - Is light color
*/
function getValue(hsv: HsvColor, i: number, isLight: boolean) {
let value: number;

View File

@ -1,9 +1,7 @@
import CryptoJS from 'crypto-js';
export class Crypto<T extends object> {
/**
* secret
*/
/** Secret */
secret: string;
constructor(secret: string) {

View File

@ -1,8 +1,6 @@
import localforage from 'localforage';
/**
* the storage type
*/
/** The storage type */
export type StorageType = 'local' | 'session';
export function createStorage<T extends object>(type: StorageType) {
@ -10,9 +8,10 @@ export function createStorage<T extends object>(type: StorageType) {
const storage = {
/**
* set session
* @param key session key
* @param value session value
* Set session
*
* @param key Session key
* @param value Session value
*/
set<K extends keyof T>(key: K, value: T[K]) {
const json = JSON.stringify(value);
@ -20,8 +19,9 @@ export function createStorage<T extends object>(type: StorageType) {
stg.setItem(key as string, json);
},
/**
* get session
* @param key session key
* Get session
*
* @param key Session key
*/
get<K extends keyof T>(key: K): T[K] | null {
const json = stg.getItem(key as string);