basic components

This commit is contained in:
Rewrite0
2023-05-13 15:07:19 +08:00
parent 736375f6bd
commit 235d2bdb8d
22 changed files with 6528 additions and 598 deletions

View File

@@ -1,7 +1,15 @@
{
"extends": ["@antfu", "prettier"],
"extends": [
"@antfu",
"prettier",
"plugin:storybook/recommended"
],
"rules": {
"antfu/if-newline": ["off"],
"no-console": ["off"]
"antfu/if-newline": [
"off"
],
"no-console": [
"off"
]
}
}

24
.storybook/main.ts Normal file
View File

@@ -0,0 +1,24 @@
import type { StorybookConfig } from '@storybook/vue3-vite';
import Unocss from 'unocss/vite';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/vue3-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
viteFinal(config) {
config.plugins?.push(Unocss());
// Add other configuration here depending on your use case
return config;
},
};
export default config;

17
.storybook/preview.ts Normal file
View File

@@ -0,0 +1,17 @@
import type { Preview } from '@storybook/vue3';
import '@unocss/reset/tailwind-compat.css';
import 'uno.css';
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;

View File

@@ -11,30 +11,44 @@
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"preview": "vite preview",
"test": "vitest"
"test": "vitest",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@headlessui/vue": "^1.7.13",
"@vueuse/core": "^8.9.4",
"axios": "^0.27.2",
"pinia": "^2.0.35",
"vue": "^3.2.47",
"vue-router": "^4.1.6"
"pinia": "^2.0.36",
"vue": "^3.3.2",
"vue-router": "^4.2.0"
},
"devDependencies": {
"@antfu/eslint-config": "^0.38.5",
"@types/node": "^18.16.0",
"@unocss/preset-rem-to-px": "^0.51.8",
"@unocss/reset": "^0.51.8",
"@antfu/eslint-config": "^0.38.6",
"@icon-park/vue-next": "^1.4.2",
"@storybook/addon-essentials": "^7.0.11",
"@storybook/addon-interactions": "^7.0.11",
"@storybook/addon-links": "^7.0.11",
"@storybook/blocks": "^7.0.11",
"@storybook/testing-library": "0.0.14-next.2",
"@storybook/vue3": "^7.0.11",
"@storybook/vue3-vite": "^7.0.11",
"@types/node": "^18.16.8",
"@unocss/preset-rem-to-px": "^0.51.12",
"@unocss/reset": "^0.51.12",
"@vitejs/plugin-vue": "^3.2.0",
"eslint": "^8.39.0",
"eslint": "^8.40.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-storybook": "^0.6.12",
"prettier": "^2.8.8",
"sass": "^1.62.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.62.1",
"storybook": "^7.0.11",
"typescript": "^4.9.5",
"unocss": "^0.51.8",
"unocss": "^0.51.12",
"unplugin-auto-import": "^0.10.3",
"unplugin-vue-components": "^0.21.2",
"unplugin-vue-components": "^0.24.1",
"unplugin-vue-router": "^0.6.4",
"vite": "^3.2.6",
"vitest": "^0.30.1",

6399
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from '@storybook/vue3';
import AbAdd from './ab-add.vue';
const meta: Meta<typeof AbAdd> = {
title: 'basic/ab-add',
component: AbAdd,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof AbAdd>;
export const Template: Story = {
render: (args) => ({
components: { AbAdd },
setup() {
return { args };
},
template: '<ab-add v-bind="args"></ab-add>',
}),
};

41
src/basic/ab-add.vue Normal file
View File

@@ -0,0 +1,41 @@
<script lang="ts" setup>
defineEmits(['click']);
</script>
<template>
<button
rounded="1/2"
wh-36px
f-cer
rel
transition-colors
class="box"
@click="$emit('click')"
>
<div class="line" abs></div>
<div class="line" abs rotate-90></div>
</button>
</template>
<style lang="scss" scoped>
$normal: #493475;
$hover: #756596;
.box {
background: $normal;
&:hover {
background: $hover;
}
&:active {
background: $normal;
}
}
.line {
width: 6px;
height: 18px;
background: #fff;
}
</style>

View File

@@ -0,0 +1,32 @@
import type { Meta, StoryObj } from '@storybook/vue3';
import AbButton from './ab-button.vue';
const meta: Meta<typeof AbButton> = {
title: 'basic/ab-button',
component: AbButton,
tags: ['autodocs'],
argTypes: {
type: {
control: { type: 'select' },
options: ['primary', 'warn'],
},
size: {
control: { type: 'select' },
options: ['big', 'normal', 'small'],
},
},
};
export default meta;
type Story = StoryObj<typeof AbButton>;
export const Template: Story = {
render: (args) => ({
components: { AbButton },
setup() {
return { args };
},
template: '<ab-button v-bind="args">button</ab-button>',
}),
};

59
src/basic/ab-button.vue Normal file
View File

@@ -0,0 +1,59 @@
<script lang="ts" setup>
const props = withDefaults(
defineProps<{
type?: 'primary' | 'warn';
size?: 'big' | 'normal' | 'small';
}>(),
{
type: 'primary',
size: 'normal',
}
);
defineEmits(['click']);
</script>
<template>
<button
text-white
outline-none
:class="[`type-${type}`, `size-${size}`]"
@click="$emit('click')"
>
<slot></slot>
</button>
</template>
<style lang="scss" scoped>
.type {
&-primary {
@include bg-mouse-event(#4e3c94, #281e52, #8e8a9c);
}
&-warn {
@include bg-mouse-event(#943c61, #521e2a, #9c8a93);
}
}
.size {
&-normal {
border-radius: 6px;
width: 171px;
height: 36px;
}
&-big {
font-size: 24px;
border-radius: 10px;
width: 276px;
height: 55px;
}
&-small {
font-size: 12px;
border-radius: 6px;
width: 86px;
height: 28px;
}
}
</style>

View File

@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from '@storybook/vue3';
import AbCheckbox from './ab-checkbox.vue';
const meta: Meta<typeof AbCheckbox> = {
title: 'basic/ab-checkbox',
component: AbCheckbox,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof AbCheckbox>;
export const Template: Story = {
render: (args) => ({
components: { AbCheckbox },
setup() {
return { args };
},
template: '<ab-checkbox v-bind="args" />',
}),
};

52
src/basic/ab-checkbox.vue Normal file
View File

@@ -0,0 +1,52 @@
<script lang="ts" setup>
import { Switch } from '@headlessui/vue';
const props = withDefaults(
defineProps<{
modelValue: boolean;
small?: boolean;
}>(),
{
modelValue: false,
small: false,
}
);
const emit = defineEmits(['update:modelValue']);
const checked = ref(props.modelValue);
watchEffect(() => {
emit('update:modelValue', checked.value);
});
</script>
<template>
<Switch v-model="checked" as="template">
<div flex items-center space-x-8px is-btn>
<slot name="before"></slot>
<div
rounded-4px
rel
f-cer
bg-white
border="3px #3c239f"
:class="[small ? 'wh-16px' : 'wh-32px', !checked && 'group']"
>
<div
rounded-2px
transition-all
duration-300
:class="[
small ? 'wh-8px' : 'wh-16px',
checked ? 'bg-[#3c239f]' : 'bg-transparent',
]"
group-hover:bg="#cccad4"
group-active:bg="#3c239f"
></div>
</div>
<slot name="after"></slot>
</div>
</Switch>
</template>

View File

@@ -0,0 +1,17 @@
<script lang="ts" setup>
const props = withDefaults(
defineProps<{
title: string;
}>(),
{
title: 'title',
}
);
</script>
<template>
<div fx-cer space-x-12px>
<div text-h1>{{ title }}</div>
<div w-160px h-3px bg-theme-row rounded-full></div>
</div>
</template>

54
src/basic/ab-search.vue Normal file
View File

@@ -0,0 +1,54 @@
<script lang="ts" setup>
import { Search } from '@icon-park/vue-next';
const props = withDefaults(
defineProps<{
value?: string;
}>(),
{
value: '',
}
);
const emit = defineEmits(['update:value', 'click-search']);
const onInput = (e: Event) => {
const input = e.target as HTMLInputElement;
emit('update:value', input.value);
};
const onSearch = () => {
emit('click-search', props.value);
};
</script>
<template>
<div
bg="#7752B4"
fx-cer
rounded-12px
h-36px
px-12px
space-x-12px
w-276px
focus-within:w-396px
transition-width
>
<search
theme="outline"
size="24"
fill="#fff"
@click="onSearch"
is-btn
btn-click
/>
<input
type="text"
:value="value"
@input="onInput"
@keyup.enter="onSearch"
input-reset
/>
</div>
</template>

View File

@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from '@storybook/vue3';
import AbSelect from './ab-select.vue';
const meta: Meta<typeof AbSelect> = {
title: 'basic/ab-select',
component: AbSelect,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof AbSelect>;
export const Template: Story = {
render: (args) => ({
components: { AbSelect },
setup() {
return { args };
},
template: '<ab-select v-bind="args" />',
}),
};

84
src/basic/ab-select.vue Normal file
View File

@@ -0,0 +1,84 @@
<script lang="ts" setup>
import {
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
} from '@headlessui/vue';
import { Down, Up } from '@icon-park/vue-next';
export interface SelectItem {
id: number;
label?: string;
value: string;
disabled?: boolean;
}
const props = withDefaults(
defineProps<{
modelValue?: SelectItem;
items: SelectItem[];
}>(),
{}
);
const emit = defineEmits(['update:modelValue']);
const selected = ref(props.modelValue || (props.items?.[0] ?? ''));
const otherItems = computed(() => {
return props?.items?.filter((e) => e.id !== selected.value.id) ?? [];
});
watchEffect(() => {
emit('update:modelValue', selected.value);
});
</script>
<template>
<Listbox v-model="selected" v-slot="{ open }">
<div
rel
flex="inline col"
rounded-6px
border="1px black"
text-main
py-8px
px-12px
>
<ListboxButton bg-transparent fx-cer space-x-24px>
<div>
{{ selected?.label ?? selected.value }}
</div>
<div :class="[{ hidden: open }]">
<Down />
</div>
</ListboxButton>
<ListboxOptions mt-8px>
<div flex="~ items-end" space-x-24px>
<div flex="~ col" space-y-8px>
<ListboxOption
v-slot="{ active }"
v-for="item in otherItems"
:key="item.id"
:value="item"
:disabled="item.disabled"
>
<div
:class="[
{ 'text-primary': active },
item.disabled ? 'is-disabled' : 'is-btn',
]"
>
{{ item.label ?? item.value }}
</div>
</ListboxOption>
</div>
<div :class="[{ hidden: !open }]"><Up /></div>
</div>
</ListboxOptions>
</div>
</Listbox>
</template>

23
src/basic/ab-status.vue Normal file
View File

@@ -0,0 +1,23 @@
<script lang="ts" setup>
const props = withDefaults(
defineProps<{
running: boolean;
}>(),
{
running: false,
}
);
</script>
<template>
<div wh-24px f-cer>
<div rounded="1/2" f-cer border="2px solid white" wh-22px>
<div
:class="[running ? 'bg-running' : 'bg-stopped']"
rounded="1/2"
wh-10px
transition-colors
></div>
</div>
</div>
</template>

View File

@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from '@storybook/vue3';
import AbSwitch from './ab-switch.vue';
const meta: Meta<typeof AbSwitch> = {
title: 'basic/ab-switch',
component: AbSwitch,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof AbSwitch>;
export const Template: Story = {
render: (args) => ({
components: { AbSwitch },
setup() {
return { args };
},
template: '<ab-switch v-bind="args" />',
}),
};

86
src/basic/ab-switch.vue Normal file
View File

@@ -0,0 +1,86 @@
<script lang="ts" setup>
import { Switch } from '@headlessui/vue';
const props = withDefaults(
defineProps<{
modelValue: boolean;
}>(),
{
modelValue: false,
}
);
const emit = defineEmits(['update:modelValue']);
const checked = ref(props.modelValue);
watchEffect(() => {
emit('update:modelValue', checked.value);
});
</script>
<template>
<Switch v-model="checked" as="template">
<div
is-btn
w-48px
h-28px
rounded-full
rel
flex="inline items-center"
transition-colors
duration-300
p-3px
shadow="~ inset"
class="box"
:class="{ checked }"
>
<div
wh-22px
rounded="1/2"
transition-all
duration-300
class="slider"
:class="{ checked, 'translate-x-20px': checked }"
></div>
</div>
</Switch>
</template>
<style lang="scss" scope>
$bg-unchecked: #929292;
$bg-checked: #e7e7e7;
$slider-unchecked: #ececef;
$slider-unchecked-hover: #dbd8ec;
$slider-checked: #1c1259;
$slider-checked-hover: #62589e;
.box {
background: $bg-unchecked;
&.checked {
background: $bg-checked;
}
&:hover .slider {
&:not(.checked) {
background: $slider-unchecked-hover;
}
&.checked {
background: $slider-checked-hover;
}
}
}
.slider {
&:not(.checked) {
background: $slider-unchecked;
}
&.checked {
background: $slider-checked;
}
}
</style>

38
src/components.d.ts vendored
View File

@@ -1,5 +1,7 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
@@ -7,32 +9,14 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ConfigFormCol: typeof import('./components/ConfigFormCol.vue')['default']
ConfigFormRow: typeof import('./components/ConfigFormRow.vue')['default']
ElAside: typeof import('element-plus/es')['ElAside']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
AbAdd: typeof import('./basic/ab-add.vue')['default']
AbButton: typeof import('./basic/ab-button.vue')['default']
AbCheckbox: typeof import('./basic/ab-checkbox.vue')['default']
AbInput: typeof import('./basic/ab-input.vue')['default']
AbSelect: typeof import('./basic/ab-select.vue')['default']
AbSwitch: typeof import('./basic/ab-switch.vue')['default']
copy: typeof import('./basic/ab-switch copy.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ShowResults: typeof import('./components/ShowResults.vue')['default']
}
}

13
src/style/mixin.scss Normal file
View File

@@ -0,0 +1,13 @@
@mixin bg-mouse-event($normal, $hover, $active) {
background: $normal;
transition: background 0.3s;
&:hover {
background: $hover;
}
&:active {
transition: none;
background: $active;
}
}

View File

@@ -13,5 +13,41 @@ export default defineConfig({
presetAttributify(),
presetIcons({ cdn: 'https://esm.sh/' }),
],
shortcuts: [[/^wh-(.*)$/, ([, t]) => `w-${t} h-${t}`]],
theme: {
colors: {
primary: '#493475',
},
},
rules: [
[
'bg-theme-row',
{
background: 'linear-gradient(90.5deg, #372A87 1.53%, #9B4D9C 96.48%)',
},
],
[
'bg-theme-col',
{
background: 'linear-gradient(180deg, #3C239F 0%, #793572 100%)',
},
],
],
shortcuts: [
[/^wh-(.*)$/, ([, t]) => `w-${t} h-${t}`],
['rel', 'relative'],
['abs', 'absolute'],
['fx-cer', 'flex items-center'],
['f-cer', 'fx-cer justify-center'],
['text-h1', 'text-24px'],
['text-h2', 'text-20px'],
['text-h3', 'text-16px'],
['text-main', 'text-12px'],
[
'ab-input',
'outline-none min-w-0 w-200px rounded-6px border-1 border-black shadow-inset hover:border-color-[#7A46AE]',
],
['input-error', 'border-color-[#CA0E0E]'],
['is-btn', 'cursor-pointer select-none'],
['is-disabled', 'cursor-not-allowed select-none'],
],
});

View File

@@ -6,7 +6,6 @@ import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import VueRouter from 'unplugin-vue-router/vite';
import { VueRouterAutoImports } from 'unplugin-vue-router';
import { HeadlessUiResolver } from 'unplugin-vue-components/resolvers';
// https://vitejs.dev/config/
export default defineConfig({
@@ -25,9 +24,15 @@ export default defineConfig({
Components({
dts: 'src/components.d.ts',
dirs: ['src/basic'],
resolvers: [HeadlessUiResolver()],
}),
],
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "./src/style/mixin.scss";',
},
},
},
build: {
cssCodeSplit: false,
},