design-system-next 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/design-system-next.js +3185 -0
- package/dist/design-system-next.js.gz +0 -0
- package/dist/main.css +1 -0
- package/dist/main.css.gz +0 -0
- package/dist/main.d.ts +9 -0
- package/package.json +85 -0
- package/src/App.vue +179 -0
- package/src/assets/scripts/borderRadius.ts +15 -0
- package/src/assets/scripts/colors.ts +134 -0
- package/src/assets/scripts/maxWidth.ts +11 -0
- package/src/assets/scripts/spacing.ts +23 -0
- package/src/assets/styles/tailwind.css +795 -0
- package/src/components/badge/badge.ts +43 -0
- package/src/components/badge/badge.vue +20 -0
- package/src/components/badge/use-badge.ts +52 -0
- package/src/components/button/button.ts +64 -0
- package/src/components/button/button.vue +25 -0
- package/src/components/button/use-button.ts +166 -0
- package/src/components/lozenge/lozenge.ts +57 -0
- package/src/components/lozenge/lozenge.vue +96 -0
- package/src/components/lozenge/use-lozenge.ts +12 -0
- package/src/components/radio/radio.ts +54 -0
- package/src/components/radio/radio.vue +36 -0
- package/src/components/radio/use-radio.ts +65 -0
- package/src/components/sidenav/sidenav.ts +43 -0
- package/src/components/sidenav/sidenav.vue +235 -0
- package/src/components/sidenav/use-sidenav.ts +31 -0
- package/src/components/switch/switch.ts +35 -0
- package/src/components/switch/switch.vue +106 -0
- package/src/components/switch/use-switch.ts +106 -0
- package/src/main.ts +13 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PropType, ExtractPropTypes } from 'vue';
|
|
2
|
+
|
|
3
|
+
export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
4
|
+
|
|
5
|
+
const BADGE_VARIANT = ['danger', 'disabled', 'information', 'brand'] as const;
|
|
6
|
+
const BADGE_SIZE = ['small', 'big', 'tiny'] as const;
|
|
7
|
+
const BADGE_POSITION = ['top', 'bottom', 'default'] as const;
|
|
8
|
+
|
|
9
|
+
export const badgePropTypes = {
|
|
10
|
+
/**
|
|
11
|
+
* @description Badge Label
|
|
12
|
+
*/
|
|
13
|
+
text: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: '0',
|
|
16
|
+
},
|
|
17
|
+
/**
|
|
18
|
+
* @description Badge variant
|
|
19
|
+
*/
|
|
20
|
+
variant: {
|
|
21
|
+
type: String,
|
|
22
|
+
validator: (value: (typeof BADGE_VARIANT)[number]) => BADGE_VARIANT.includes(value),
|
|
23
|
+
default: 'brand',
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* @description Badge size
|
|
27
|
+
*/
|
|
28
|
+
size: {
|
|
29
|
+
type: String,
|
|
30
|
+
validator: (value: (typeof BADGE_SIZE)[number]) => BADGE_SIZE.includes(value),
|
|
31
|
+
default: 'small',
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* @description Badge position (top, bottom, default)
|
|
35
|
+
*/
|
|
36
|
+
position: {
|
|
37
|
+
type: String,
|
|
38
|
+
validator: (value: (typeof BADGE_POSITION)[number]) => BADGE_POSITION.includes(value),
|
|
39
|
+
default: 'default',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type BadgePropTypes = ExtractPropTypes<typeof badgePropTypes>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tw-flex tw-gap-2">
|
|
3
|
+
<slot />
|
|
4
|
+
<div :class="[badgeElementWrapper]">
|
|
5
|
+
<section :class="[badgePositionClasses]">
|
|
6
|
+
<div :class="[badgeClasses, 'tw-flex tw-items-center tw-justify-center']">
|
|
7
|
+
{{ props.size === 'tiny' ? '' : text }}
|
|
8
|
+
</div>
|
|
9
|
+
</section>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { badgePropTypes } from './badge';
|
|
16
|
+
import { useBadge } from './use-badge';
|
|
17
|
+
|
|
18
|
+
const props = defineProps(badgePropTypes);
|
|
19
|
+
const { badgeClasses, badgePositionClasses, badgeElementWrapper } = useBadge(props);
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import type { BadgePropTypes } from './badge';
|
|
3
|
+
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
export const useBadge = (props: BadgePropTypes) => {
|
|
6
|
+
const { position, size, variant } = props as {
|
|
7
|
+
size: 'big' | 'small' | 'tiny';
|
|
8
|
+
variant: 'danger' | 'disabled' | 'information' | 'brand';
|
|
9
|
+
position: 'top' | 'bottom' | 'default';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const badgeClasses = computed(() => {
|
|
13
|
+
const variantClasses = classNames({
|
|
14
|
+
'tw-background-color-danger-base tw-text-color-inverted-strong': variant === 'danger',
|
|
15
|
+
'tw-background-color-disabled tw-text-color-on-fill-disabled ': variant === 'disabled',
|
|
16
|
+
'tw-background-color-information-base tw-text-color-inverted-strong': variant === 'information',
|
|
17
|
+
'tw-background-color-brand-base tw-text-color-inverted-strong': variant === 'brand',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const sizeClasses = classNames({
|
|
21
|
+
'tw-label-sm-medium tw-h-[20px] tw-min-w-[20px] tw-rounded-[32px]': size === 'big',
|
|
22
|
+
'tw-label-xs-medium tw-h-[16px] tw-min-w-[16px] tw-rounded-[32px]': size === 'small',
|
|
23
|
+
'tw-h-[10px] tw-min-w-[10px] tw-rounded-full': size === 'tiny',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return classNames(variantClasses, sizeClasses);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const badgePositionClasses = computed(() => {
|
|
30
|
+
return classNames({
|
|
31
|
+
'tw-absolute tw--top-1 tw-right-1': position === 'top' && size === 'tiny',
|
|
32
|
+
' tw-absolute tw--bottom-1 tw-right-1': position === 'bottom' && size === 'tiny',
|
|
33
|
+
'tw-absolute tw--top-2 tw--right-1': position === 'top' && size === 'small',
|
|
34
|
+
' tw-absolute tw--bottom-2 tw--right-1': position === 'bottom' && size === 'small',
|
|
35
|
+
'tw-absolute tw--top-3 tw--right-2': position === 'top' && size === 'big',
|
|
36
|
+
' tw-absolute tw--bottom-3 tw--right-2': position === 'bottom' && size === 'big',
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const badgeElementWrapper = computed(() => {
|
|
41
|
+
return classNames({
|
|
42
|
+
'tw-flex tw-items-center tw-gap-2 ': position === 'default',
|
|
43
|
+
'tw-relative': position === 'top' || position === 'bottom',
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
badgeClasses,
|
|
49
|
+
badgePositionClasses,
|
|
50
|
+
badgeElementWrapper,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { PropType, ExtractPropTypes } from 'vue';
|
|
2
|
+
|
|
3
|
+
export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
4
|
+
|
|
5
|
+
const BUTTON_TONES = ['neutral', 'success', 'danger'] as const;
|
|
6
|
+
const BUTTON_SIZES = ['small', 'medium', 'large'] as const;
|
|
7
|
+
const BUTTON_TYPES = ['button', 'submit', 'reset'] as const;
|
|
8
|
+
const BUTTON_STATES = ['base', 'hover', 'pressed', 'focus'] as const;
|
|
9
|
+
const BUTTON_VARIANTS = ['primary', 'secondary', 'tertiary'] as const;
|
|
10
|
+
|
|
11
|
+
export const buttonPropTypes = {
|
|
12
|
+
/**
|
|
13
|
+
* @description Button tone
|
|
14
|
+
*/
|
|
15
|
+
tone: {
|
|
16
|
+
type: String as PropType<(typeof BUTTON_TONES)[number]>,
|
|
17
|
+
validator: (value: (typeof BUTTON_TONES)[number]) => BUTTON_TONES.includes(value),
|
|
18
|
+
default: 'neutral',
|
|
19
|
+
},
|
|
20
|
+
/**
|
|
21
|
+
* @description Button size
|
|
22
|
+
*/
|
|
23
|
+
size: {
|
|
24
|
+
type: String as PropType<(typeof BUTTON_SIZES)[number]>,
|
|
25
|
+
validator: (value: (typeof BUTTON_SIZES)[number]) => BUTTON_SIZES.includes(value),
|
|
26
|
+
default: 'medium',
|
|
27
|
+
},
|
|
28
|
+
/**
|
|
29
|
+
* @description Native button type
|
|
30
|
+
*/
|
|
31
|
+
type: {
|
|
32
|
+
type: String as PropType<(typeof BUTTON_TYPES)[number]>,
|
|
33
|
+
validator: (value: (typeof BUTTON_TYPES)[number]) => BUTTON_TYPES.includes(value),
|
|
34
|
+
default: 'button',
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* @description Button state
|
|
38
|
+
*/
|
|
39
|
+
state: {
|
|
40
|
+
type: String as PropType<(typeof BUTTON_STATES)[number]>,
|
|
41
|
+
validator: (value: (typeof BUTTON_STATES)[number]) => BUTTON_STATES.includes(value),
|
|
42
|
+
default: 'base',
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* @description Button Variant
|
|
46
|
+
*/
|
|
47
|
+
variant: {
|
|
48
|
+
type: String as PropType<(typeof BUTTON_VARIANTS)[number]>,
|
|
49
|
+
validator: (value: (typeof BUTTON_VARIANTS)[number]) => BUTTON_VARIANTS.includes(value),
|
|
50
|
+
default: 'primary',
|
|
51
|
+
},
|
|
52
|
+
disabled: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: false,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const buttonEmitTypes = {
|
|
59
|
+
click: (evt: MouseEvent): evt is MouseEvent => evt instanceof MouseEvent,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type ButtonPropTypes = ExtractPropTypes<typeof buttonPropTypes>;
|
|
63
|
+
export type ButtonEmitTypes = typeof buttonEmitTypes;
|
|
64
|
+
export type ButtonType = ButtonPropTypes['type'];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
ref="buttonRef"
|
|
4
|
+
v-bind="buttonProps"
|
|
5
|
+
:class="[
|
|
6
|
+
'tw-inline-flex tw-w-fit tw-min-w-[56px] tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-md tw-outline-none tw-duration-150 tw-ease-in-out',
|
|
7
|
+
'hover:tw-shadow-button-hover',
|
|
8
|
+
'active:tw-scale-95',
|
|
9
|
+
buttonClass,
|
|
10
|
+
]"
|
|
11
|
+
@click="handleClick"
|
|
12
|
+
>
|
|
13
|
+
<slot />
|
|
14
|
+
</button>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script lang="ts" setup>
|
|
18
|
+
import { buttonEmitTypes, buttonPropTypes } from './button';
|
|
19
|
+
import { useButton } from './use-button';
|
|
20
|
+
|
|
21
|
+
const props = defineProps(buttonPropTypes);
|
|
22
|
+
const emit = defineEmits(buttonEmitTypes);
|
|
23
|
+
|
|
24
|
+
const { buttonRef, buttonProps, buttonClass, handleClick } = useButton(props, emit);
|
|
25
|
+
</script>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { computed, ref, ComputedRef } from 'vue';
|
|
2
|
+
import { useElementHover, useMousePressed, useFocus } from '@vueuse/core';
|
|
3
|
+
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
|
|
6
|
+
import type { SetupContext } from 'vue';
|
|
7
|
+
import type { ButtonEmitTypes, ButtonPropTypes } from './button';
|
|
8
|
+
|
|
9
|
+
export const useButton = (props: ButtonPropTypes, emit: SetupContext<ButtonEmitTypes>['emit']) => {
|
|
10
|
+
const buttonRef = ref<HTMLButtonElement | null>(null);
|
|
11
|
+
const isHovered = useElementHover(buttonRef);
|
|
12
|
+
const { pressed } = useMousePressed({ target: buttonRef });
|
|
13
|
+
const { focused } = useFocus(buttonRef);
|
|
14
|
+
const { state, type, size, tone, variant, disabled } = props;
|
|
15
|
+
|
|
16
|
+
const buttonProps: ComputedRef<Record<string, unknown>> = computed(() => {
|
|
17
|
+
return {
|
|
18
|
+
...(disabled && { ariaDisabled: true }),
|
|
19
|
+
disabled: disabled,
|
|
20
|
+
autofocus: state === 'focus',
|
|
21
|
+
type: type ?? 'button',
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const buttonSizeCssClass: ComputedRef<string> = computed(() =>
|
|
26
|
+
classNames({
|
|
27
|
+
'tw-px-[4px] tw-py-[6px] tw-font-medium tw-font-size-100 tw-leading-100': size === 'small',
|
|
28
|
+
'tw-p-[8px] tw-font-medium tw-font-size-100 tw-leading-100': size === 'medium',
|
|
29
|
+
'tw-px-[8px] tw-py-[12px] tw-font-medium tw-font-size-200 tw-leading-300': size === 'large',
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const buttonTextCssClass: ComputedRef<string> = computed(() => {
|
|
34
|
+
if (variant === 'secondary' || variant === 'tertiary') {
|
|
35
|
+
return classNames({
|
|
36
|
+
'tw-text-color-strong': tone === 'neutral',
|
|
37
|
+
'tw-text-color-brand-base': tone === 'success',
|
|
38
|
+
'tw-text-color-danger-base': tone === 'danger',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return classNames({
|
|
43
|
+
'tw-text-color-strong': tone === 'neutral',
|
|
44
|
+
'tw-text-color-inverted-strong': tone === 'success' || tone === 'danger',
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// #region - Background Css Class
|
|
49
|
+
const buttonBackgroundCssClass: ComputedRef<string> = computed(() => {
|
|
50
|
+
if (variant === 'secondary') {
|
|
51
|
+
return isHovered.value ? 'tw-background-color-hover' : '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (variant === 'tertiary') {
|
|
55
|
+
return getTertiaryBackground();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return getBackgroundBasedOnState();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
function getTertiaryBackground(): string {
|
|
62
|
+
if (pressed.value) {
|
|
63
|
+
return 'tw-background-color-pressed';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return isHovered.value ? 'tw-background-color-hover' : '';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getBackgroundBasedOnState(): string {
|
|
70
|
+
if (pressed.value) {
|
|
71
|
+
return getPressedBackground();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (isHovered.value) {
|
|
75
|
+
return getHoveredBackground();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return getDefaultBackground();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getPressedBackground(): string {
|
|
82
|
+
const backgrounds: Record<string, string> = {
|
|
83
|
+
neutral: 'tw-background-color-pressed',
|
|
84
|
+
success: 'tw-background-color-brand-pressed',
|
|
85
|
+
danger: 'tw-background-color-danger-pressed',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return backgrounds[tone] || '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getHoveredBackground(): string {
|
|
92
|
+
const backgrounds: Record<string, string> = {
|
|
93
|
+
neutral: 'tw-background-color-hover',
|
|
94
|
+
success: 'tw-background-color-success-pressed',
|
|
95
|
+
danger: 'tw-background-color-danger-hover',
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return backgrounds[tone] || '';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getDefaultBackground(): string {
|
|
102
|
+
const backgrounds: Record<string, string> = {
|
|
103
|
+
neutral: 'tw-background-color-base',
|
|
104
|
+
success: 'tw-background-color-brand-base',
|
|
105
|
+
danger: 'tw-background-color-danger-base',
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return backgrounds[tone] || '';
|
|
109
|
+
}
|
|
110
|
+
// #endregion - Background Css Class
|
|
111
|
+
|
|
112
|
+
const buttonBorderCssClass: ComputedRef<string> = computed(() => {
|
|
113
|
+
if (variant === 'primary' || variant === 'tertiary') {
|
|
114
|
+
if (focused.value) {
|
|
115
|
+
return 'tw-border-solid tw-border tw-border-white-50';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return 'tw-border-solid tw-border tw-border-transparent';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return classNames({
|
|
122
|
+
'tw-border-solid tw-border tw-border-color-base': tone === 'neutral',
|
|
123
|
+
'tw-border-solid tw-border tw-border-color-brand-base': tone === 'success',
|
|
124
|
+
'tw-border-solid tw-border tw-border-color-danger-base': tone === 'danger',
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const buttonToneCssClass: ComputedRef<string> = computed(() => {
|
|
129
|
+
return classNames(buttonBackgroundCssClass.value, buttonTextCssClass.value, buttonBorderCssClass.value);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const buttonShadowClass: ComputedRef<string> = computed(() => {
|
|
133
|
+
if (pressed.value) {
|
|
134
|
+
return 'tw-shadow-button';
|
|
135
|
+
} else if (focused.value) {
|
|
136
|
+
return 'tw-shadow-button-active';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return '';
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const buttonAllCssClass: ComputedRef<string> = computed(() => {
|
|
143
|
+
if (disabled) {
|
|
144
|
+
return classNames(buttonSizeCssClass.value, 'tw-text-color-disabled');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return classNames(buttonSizeCssClass.value, buttonToneCssClass.value, buttonShadowClass.value);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const handleClick = (evt: MouseEvent) => {
|
|
151
|
+
if (disabled) {
|
|
152
|
+
evt.stopPropagation();
|
|
153
|
+
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
emit('click', evt);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
buttonRef,
|
|
162
|
+
buttonProps,
|
|
163
|
+
buttonClass: buttonAllCssClass,
|
|
164
|
+
handleClick,
|
|
165
|
+
};
|
|
166
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { PropType, ExtractPropTypes } from 'vue';
|
|
2
|
+
|
|
3
|
+
export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
4
|
+
|
|
5
|
+
const LOZENGE_TONE = ['pending', 'information', 'success', 'danger', 'neutral', 'caution'] as const;
|
|
6
|
+
|
|
7
|
+
export const lozengePropTypes = {
|
|
8
|
+
/**
|
|
9
|
+
* @description Lozenge Label
|
|
10
|
+
*/
|
|
11
|
+
label: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: 'label',
|
|
14
|
+
},
|
|
15
|
+
/**
|
|
16
|
+
* @description Lozenge tone
|
|
17
|
+
*/
|
|
18
|
+
tone: {
|
|
19
|
+
type: String as PropType<(typeof LOZENGE_TONE)[number]>,
|
|
20
|
+
validator: (value: (typeof LOZENGE_TONE)[number]) => LOZENGE_TONE.includes(value),
|
|
21
|
+
default: 'plain',
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* @description Lozenge type (fill or outline)
|
|
25
|
+
*/
|
|
26
|
+
fill: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
default: false,
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* @description Lozenge removable
|
|
32
|
+
*/
|
|
33
|
+
removable: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false,
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* @description avatar image url
|
|
39
|
+
*/
|
|
40
|
+
url: {
|
|
41
|
+
type: String,
|
|
42
|
+
default: '',
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* @description handler if the lozenge is visible
|
|
46
|
+
*/
|
|
47
|
+
visible: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: true,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type LozengePropTypes = ExtractPropTypes<typeof lozengePropTypes>;
|
|
54
|
+
export const removeEmitTypes = {
|
|
55
|
+
onRemove: (evt: MouseEvent): evt is MouseEvent => evt instanceof MouseEvent,
|
|
56
|
+
};
|
|
57
|
+
export type RemoveEmitTypes = typeof removeEmitTypes;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="visible" :class="[fill ? 'lozenge-fill' : 'lozenge']">
|
|
3
|
+
<div
|
|
4
|
+
:class="[
|
|
5
|
+
'tw-label-xs-medium tw-inline-flex tw-items-center tw-gap-size-spacing-6xs tw-rounded-md tw-border tw-border-solid tw-p-size-spacing-5xs tw-text-xs tw-uppercase',
|
|
6
|
+
tone,
|
|
7
|
+
]"
|
|
8
|
+
>
|
|
9
|
+
<div v-if="$slots.icon" class="tw-flex tw-h-3 tw-w-3 tw-items-center tw-overflow-hidden">
|
|
10
|
+
<slot name="icon" />
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div v-if="$slots.avatar" class="tw-flex tw-items-center">
|
|
14
|
+
<slot name="avatar" />
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div v-if="url && !$slots.avatar" class="tw-h-4 tw-w-4 tw-overflow-hidden">
|
|
18
|
+
<img class="tw-h-full tw-w-full tw-rounded-full tw-object-cover" :src="url" alt="avatar" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div>{{ label }}</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import { lozengePropTypes } from './lozenge';
|
|
28
|
+
|
|
29
|
+
defineProps(lozengePropTypes);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<style lang="scss" scoped>
|
|
33
|
+
.lozenge {
|
|
34
|
+
@apply tw-flex tw-flex-wrap tw-rounded-md;
|
|
35
|
+
|
|
36
|
+
.pending {
|
|
37
|
+
@apply tw-text-color-pending-base tw-background-color-pending-weak tw-border-color-pending-base;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.information {
|
|
41
|
+
@apply tw-text-color-information-base tw-background-color-information-weak tw-border-color-information-base;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.success {
|
|
45
|
+
@apply tw-text-color-success-base tw-background-color-success-weak tw-border-color-success-base;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.neutral {
|
|
49
|
+
@apply tw-text-color-base tw-background-color-base tw-border-color-base;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.danger {
|
|
53
|
+
@apply tw-text-color-danger-base tw-background-color-danger-weak tw-border-color-danger-base;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.caution {
|
|
57
|
+
@apply tw-text-color-caution-base tw-background-color-caution-weak tw-border-color-caution-base;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.plain {
|
|
61
|
+
@apply tw-text-color-strong tw-border-color-base tw-background-color;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.lozenge-fill {
|
|
66
|
+
@apply tw-flex tw-flex-wrap;
|
|
67
|
+
|
|
68
|
+
.pending {
|
|
69
|
+
@apply tw-background-color-pending-base tw-text-color-strong tw-border-none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.information {
|
|
73
|
+
@apply tw-background-color-information-base tw-text-color-inverted-strong tw-border-none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.success {
|
|
77
|
+
@apply tw-background-color-success-base tw-text-color-inverted-strong;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.danger {
|
|
81
|
+
@apply tw-background-color-danger-base tw-text-color-inverted-strong tw-border-none;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.neutral {
|
|
85
|
+
@apply tw-background-color-base tw-text-color-strong tw-border-none;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.caution {
|
|
89
|
+
@apply tw-background-color-caution-base tw-text-color-strong tw-border-none;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.plain {
|
|
93
|
+
@apply tw-text-color-strong tw-background-color tw-border-none;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SetupContext } from 'vue';
|
|
2
|
+
import type { RemoveEmitTypes } from './lozenge';
|
|
3
|
+
|
|
4
|
+
export const useLozenge = (emit: SetupContext<RemoveEmitTypes>['emit']) => {
|
|
5
|
+
const hanndleRemoveLozenge = (evt: MouseEvent) => {
|
|
6
|
+
emit('onRemove', evt);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
hanndleRemoveLozenge,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { PropType, ExtractPropTypes } from 'vue';
|
|
2
|
+
|
|
3
|
+
export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
4
|
+
|
|
5
|
+
const RADIO_STATES = ['default', 'hover', 'disabled'] as const;
|
|
6
|
+
|
|
7
|
+
export const radioPropTypes = {
|
|
8
|
+
/**
|
|
9
|
+
* @description Radio state
|
|
10
|
+
*/
|
|
11
|
+
state: {
|
|
12
|
+
type: String as PropType<(typeof RADIO_STATES)[number]>,
|
|
13
|
+
validator: (value: (typeof RADIO_STATES)[number]) => RADIO_STATES.includes(value),
|
|
14
|
+
default: 'default',
|
|
15
|
+
},
|
|
16
|
+
disabled: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
default: false,
|
|
19
|
+
},
|
|
20
|
+
/**
|
|
21
|
+
* @description Attribute id
|
|
22
|
+
*/
|
|
23
|
+
id: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* @description Value for v-model
|
|
29
|
+
*/
|
|
30
|
+
modelValue: {
|
|
31
|
+
type: [String, Number, Boolean],
|
|
32
|
+
required: true,
|
|
33
|
+
default: false,
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* @description Attribute name
|
|
37
|
+
*/
|
|
38
|
+
name: {
|
|
39
|
+
type: String,
|
|
40
|
+
required: true,
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* @description Attribute value
|
|
44
|
+
*/
|
|
45
|
+
value: {
|
|
46
|
+
type: [String, Number, Boolean],
|
|
47
|
+
required: true,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const radioEmitTypes = ['update:modelValue'];
|
|
52
|
+
|
|
53
|
+
export type RadioPropTypes = ExtractPropTypes<typeof radioPropTypes>;
|
|
54
|
+
export type SwitchEmitTypes = typeof radioEmitTypes;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<input
|
|
3
|
+
:id="props.id"
|
|
4
|
+
ref="radioRef"
|
|
5
|
+
v-model="proxyValue"
|
|
6
|
+
type="radio"
|
|
7
|
+
:name="props.name"
|
|
8
|
+
:value="props.value"
|
|
9
|
+
:disabled="props.disabled"
|
|
10
|
+
:class="radioClasses"
|
|
11
|
+
/>
|
|
12
|
+
<label
|
|
13
|
+
ref="radioRef"
|
|
14
|
+
:for="props.id"
|
|
15
|
+
:disabled="props.disabled"
|
|
16
|
+
:class="[
|
|
17
|
+
'tw-flex tw-items-center tw-space-x-2',
|
|
18
|
+
radioLabelClasses
|
|
19
|
+
]"
|
|
20
|
+
>
|
|
21
|
+
<span :class="indicatorClasses"></span>
|
|
22
|
+
<slot />
|
|
23
|
+
</label>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script lang="ts" setup>
|
|
27
|
+
import { radioPropTypes, radioEmitTypes } from "./radio";
|
|
28
|
+
import { useVModel } from "@vueuse/core";
|
|
29
|
+
import { useRadioButton } from "./use-radio";
|
|
30
|
+
|
|
31
|
+
const props = defineProps(radioPropTypes);
|
|
32
|
+
const emit = defineEmits(radioEmitTypes);
|
|
33
|
+
|
|
34
|
+
const proxyValue = useVModel(props, "modelValue", emit);
|
|
35
|
+
const { radioRef, radioClasses, indicatorClasses, radioLabelClasses } = useRadioButton(props);
|
|
36
|
+
</script>
|