design-system-next 1.2.25 → 1.3.2
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/dist/design-system-next.js +2937 -2937
- package/dist/design-system-next.js.gz +0 -0
- package/dist/main.css +1 -1
- package/dist/main.css.gz +0 -0
- package/dist/main.d.ts +1 -6
- package/package.json +1 -1
- package/src/App.vue +8 -8
- package/src/assets/styles/tailwind.css +366 -367
- package/src/components/avatar/avatar.vue +1 -1
- package/src/components/avatar/use-avatar.ts +40 -34
- package/src/components/badge/badge.vue +2 -2
- package/src/components/badge/use-badge.ts +16 -16
- package/src/components/button/use-button.ts +39 -34
- package/src/components/checkbox/checkbox.vue +2 -2
- package/src/components/checkbox/use-checkbox.ts +23 -20
- package/src/components/dropdown/dropdown.vue +6 -6
- package/src/components/dropdown/use-dropdown.ts +6 -5
- package/src/components/empty-state/empty-state.vue +6 -5
- package/src/components/empty-state/use-empty-state.ts +8 -6
- package/src/components/input/input.ts +6 -1
- package/src/components/input/input.vue +7 -4
- package/src/components/input/use-input.ts +49 -47
- package/src/components/lozenge/lozenge.vue +7 -5
- package/src/components/lozenge/use-lozenge.ts +23 -15
- package/src/components/modal/modal.vue +8 -8
- package/src/components/modal/use-modal.ts +8 -8
- package/src/components/radio/radio.vue +1 -1
- package/src/components/radio/use-radio.ts +15 -13
- package/src/components/sidenav/sidenav.vue +158 -138
- package/src/components/sidepanel/sidepanel.vue +29 -21
- package/src/components/sidepanel/use-sidepanel.ts +17 -15
- package/src/components/snackbar/snack/snack.vue +13 -40
- package/src/components/snackbar/snack/use-snack.ts +12 -11
- package/src/components/snackbar/snackbar.vue +11 -13
- package/src/components/switch/switch.vue +14 -8
- package/src/components/switch/use-switch.ts +16 -15
- package/src/components/table/table.ts +5 -0
- package/src/components/table/table.vue +51 -24
- package/src/components/table/use-table.ts +1 -0
- package/src/components/tabs/tabs.vue +23 -19
- package/src/components/tabs/use-tabs.ts +5 -4
- package/src/components/timePicker/timePicker.vue +10 -9
- package/src/components/timePicker/use-timePicker.ts +31 -45
- package/src/components/tooltip/tooltip.vue +1 -1
- package/src/main.ts +3 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
3
|
v-if="isOpen && hasBackdrop"
|
|
4
|
-
class="w-screen h-screen bg-mushroom-700/60 fixed top-0 left-0 z-[30]"
|
|
4
|
+
class="spr-w-screen spr-h-screen spr-bg-mushroom-700/60 spr-fixed spr-top-0 spr-left-0 spr-z-[30]"
|
|
5
5
|
@click="handleBackdropClick"
|
|
6
6
|
></div>
|
|
7
|
-
<Transition
|
|
8
|
-
|
|
9
|
-
enter-active-class="transition-transform duration-[
|
|
10
|
-
leave-active-class="transition-transform duration-[
|
|
7
|
+
<Transition
|
|
8
|
+
name="sidepanel"
|
|
9
|
+
enter-active-class="spr-transition-transform spr-duration-[150ms] spr-ease-[ease-in-out]"
|
|
10
|
+
leave-active-class="spr-transition-transform spr-duration-[150ms] spr-ease-[ease-in-out]"
|
|
11
11
|
:enter-from-class="sidepanelStartEndState"
|
|
12
12
|
:leave-to-class="sidepanelStartEndState"
|
|
13
13
|
:enter-to-class="sidepanelMidState"
|
|
@@ -19,42 +19,50 @@
|
|
|
19
19
|
ref="sidepanelRef"
|
|
20
20
|
:class="[
|
|
21
21
|
sidepanelSizesClasses,
|
|
22
|
-
'h-[calc(
|
|
22
|
+
'spr-h-[calc(100vh-32px)] spr-bg-white-50 spr-pr-rounded-border-radius-xl spr-fixed spr-right-4 spr-z-[30] spr-min-h-[200px] spr-flex spr-flex-col spr-top-1/2 spr-translate-y-[-50%] spr-drop-shadow',
|
|
23
23
|
]"
|
|
24
24
|
:style="{ height: typeof height === 'number' ? `${height}px` : height }"
|
|
25
25
|
>
|
|
26
26
|
<template v-if="!hideHeader">
|
|
27
|
-
<div
|
|
27
|
+
<div
|
|
28
|
+
v-if="!$slots.header"
|
|
29
|
+
class="spr-tw-min-h-12 spr-subheading-xs spr-text-color-strong spr-flex spr-justify-between spr-border-0 spr-border-b spr-border-solid spr-border-mushroom-200 spr-p-4"
|
|
30
|
+
>
|
|
28
31
|
{{ headerTitle }}
|
|
29
|
-
<Icon
|
|
30
|
-
class="cursor-pointer"
|
|
31
|
-
icon="ph:x"
|
|
32
|
-
@click="handleClose"
|
|
33
|
-
/>
|
|
32
|
+
<Icon class="spr-cursor-pointer" icon="ph:x" @click="handleClose" />
|
|
34
33
|
</div>
|
|
35
34
|
<div v-else>
|
|
36
35
|
<slot name="header"></slot>
|
|
37
36
|
</div>
|
|
38
37
|
</template>
|
|
39
|
-
<div :class="['overflow-y-auto p-4', {'mb-[52px]': $slots.footer}]">
|
|
40
|
-
<slot>
|
|
41
|
-
Sidepanel Content
|
|
42
|
-
</slot>
|
|
38
|
+
<div :class="['spr-overflow-y-auto spr-p-4', { 'spr-mb-[52px]': $slots.footer }]">
|
|
39
|
+
<slot> Sidepanel Content </slot>
|
|
43
40
|
</div>
|
|
44
|
-
<div
|
|
41
|
+
<div
|
|
42
|
+
v-if="$slots.footer"
|
|
43
|
+
class="spr-absolute spr-bottom-0 spr-left-0 spr-w-full spr-rounded-b-border-radius-xl spr-border-0 spr-border-t spr-border-solid spr-border-mushroom-200 spr-bg-white-50 spr-py-3"
|
|
44
|
+
>
|
|
45
45
|
<slot name="footer"></slot>
|
|
46
46
|
</div>
|
|
47
47
|
</div>
|
|
48
48
|
</Transition>
|
|
49
|
-
|
|
50
49
|
</template>
|
|
50
|
+
|
|
51
51
|
<script lang="ts" setup>
|
|
52
|
+
import { Icon } from '@iconify/vue';
|
|
53
|
+
|
|
52
54
|
import { useSidepanel } from './use-sidepanel';
|
|
53
55
|
import { sidepanelPropTypes, sidepanelEmitTypes } from './sidepanel';
|
|
54
|
-
import { Icon } from '@iconify/vue';
|
|
55
56
|
|
|
56
57
|
const props = defineProps(sidepanelPropTypes);
|
|
57
58
|
const emit = defineEmits(sidepanelEmitTypes);
|
|
58
59
|
|
|
59
|
-
const {
|
|
60
|
+
const {
|
|
61
|
+
sidepanelRef,
|
|
62
|
+
sidepanelSizesClasses,
|
|
63
|
+
sidepanelMidState,
|
|
64
|
+
sidepanelStartEndState,
|
|
65
|
+
handleClose,
|
|
66
|
+
handleBackdropClick,
|
|
67
|
+
} = useSidepanel(props, emit);
|
|
60
68
|
</script>
|
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
import { ref, computed, toRefs, watch, onMounted, onUnmounted } from 'vue';
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
+
|
|
4
5
|
import type { SetupContext } from 'vue';
|
|
5
6
|
import type { SidepanelPropTypes, SidepanelEmitTypes } from './sidepanel';
|
|
6
7
|
|
|
7
8
|
export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<SidepanelEmitTypes>['emit']) => {
|
|
8
9
|
const sidepanelRef = ref<HTMLDivElement | null>(null);
|
|
10
|
+
|
|
9
11
|
const { size, position } = toRefs(props);
|
|
10
12
|
|
|
11
13
|
const sidepanelSizesClasses = computed(() => {
|
|
12
14
|
return classNames({
|
|
13
|
-
'w-[360px]': size.value === 'sm',
|
|
14
|
-
'w-[420px]': size.value === 'md',
|
|
15
|
-
'w-[480px]': size.value === 'lg',
|
|
15
|
+
'spr-w-[360px]': size.value === 'sm',
|
|
16
|
+
'spr-w-[420px]': size.value === 'md',
|
|
17
|
+
'spr-w-[480px]': size.value === 'lg',
|
|
16
18
|
});
|
|
17
19
|
});
|
|
18
20
|
|
|
19
21
|
const sidepanelStartEndState = computed(() => {
|
|
20
22
|
return classNames({
|
|
21
|
-
'translate-x-full -translate-y-2/4': position.value === 'right'
|
|
22
|
-
})
|
|
23
|
-
})
|
|
23
|
+
'spr-translate-x-full -spr-translate-y-2/4': position.value === 'right',
|
|
24
|
+
});
|
|
25
|
+
});
|
|
24
26
|
|
|
25
27
|
const sidepanelMidState = computed(() => {
|
|
26
28
|
return classNames({
|
|
27
|
-
'translate-x-0 -translate-y-2/4': position.value === 'right'
|
|
28
|
-
})
|
|
29
|
-
})
|
|
29
|
+
'spr-translate-x-0 -spr-translate-y-2/4': position.value === 'right',
|
|
30
|
+
});
|
|
31
|
+
});
|
|
30
32
|
|
|
31
33
|
const handleClose = () => {
|
|
32
34
|
emit('close');
|
|
@@ -34,16 +36,16 @@ export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<Sidep
|
|
|
34
36
|
|
|
35
37
|
const handleBackdropClick = () => {
|
|
36
38
|
if (props.closeOutside) {
|
|
37
|
-
emit('close')
|
|
39
|
+
emit('close');
|
|
38
40
|
}
|
|
39
|
-
}
|
|
41
|
+
};
|
|
40
42
|
|
|
41
43
|
let ignoreClick = false;
|
|
42
44
|
|
|
43
45
|
const handleClickOutside = (event: MouseEvent) => {
|
|
44
46
|
if (ignoreClick) return;
|
|
45
47
|
if (sidepanelRef.value && !sidepanelRef.value.contains(event.target as Node) && props.closeOutside) {
|
|
46
|
-
emit('close')
|
|
48
|
+
emit('close');
|
|
47
49
|
}
|
|
48
50
|
};
|
|
49
51
|
|
|
@@ -76,6 +78,6 @@ export const useSidepanel = (props: SidepanelPropTypes, emit: SetupContext<Sidep
|
|
|
76
78
|
sidepanelStartEndState,
|
|
77
79
|
handleClose,
|
|
78
80
|
handleBackdropClick,
|
|
79
|
-
handleClickOutside
|
|
80
|
-
}
|
|
81
|
-
}
|
|
81
|
+
handleClickOutside,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
@@ -1,48 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
3
|
v-bind="snackProps"
|
|
4
|
-
ref="snackRef"
|
|
5
|
-
class="
|
|
6
|
-
snackbar
|
|
7
|
-
background-color-inverted
|
|
8
|
-
text-color-inverted-strong
|
|
9
|
-
font-size-200
|
|
10
|
-
line-height-400
|
|
11
|
-
h-fit
|
|
12
|
-
rounded-border-radius-lg
|
|
13
|
-
px-size-spacing-2xs
|
|
14
|
-
py-size-spacing-3xs
|
|
15
|
-
border
|
|
16
|
-
border-solid
|
|
17
|
-
border-color-strong
|
|
18
|
-
box-border
|
|
19
|
-
flex
|
|
20
|
-
flex-row
|
|
21
|
-
items-center
|
|
22
|
-
"
|
|
4
|
+
ref="snackRef"
|
|
5
|
+
class="snackbar spr-background-color-inverted spr-text-color-inverted-strong spr-font-size-200 spr-line-height-400 spr-h-fit spr-rounded-border-radius-lg spr-px-size-spacing-2xs spr-py-size-spacing-3xs spr-border spr-border-solid spr-border-color-strong spr-box-border spr-flex spr-flex-row spr-items-center"
|
|
23
6
|
@click="handleClick"
|
|
24
7
|
>
|
|
25
|
-
<label
|
|
26
|
-
|
|
27
|
-
:class="[
|
|
28
|
-
snackToneCssClass,
|
|
29
|
-
'mr-size-spacing-3xs',
|
|
30
|
-
]"
|
|
31
|
-
>
|
|
32
|
-
<Icon :icon="snackIcon" :width="iconSize" :height="iconSize"/>
|
|
8
|
+
<label v-if="showIcon" :class="[snackToneCssClass, 'spr-mr-size-spacing-3xs']">
|
|
9
|
+
<Icon :icon="snackIcon" :width="iconSize" :height="iconSize" />
|
|
33
10
|
</label>
|
|
34
11
|
<label class="flex-auto">{{ text }}</label>
|
|
35
12
|
<div></div>
|
|
36
|
-
<label
|
|
37
|
-
v-if="showAction"
|
|
38
|
-
:class="[
|
|
13
|
+
<label
|
|
14
|
+
v-if="showAction"
|
|
15
|
+
:class="[
|
|
39
16
|
snackToneCssClass,
|
|
40
|
-
'ml-size-spacing-3xs',
|
|
41
|
-
'cursor-pointer',
|
|
42
|
-
'uppercase',
|
|
43
|
-
'font-medium',
|
|
44
|
-
'font-size-100',
|
|
45
|
-
'line-height-100'
|
|
17
|
+
'spr-ml-size-spacing-3xs selection:spr-cursor-pointer spr-uppercase spr-font-medium spr-font-size-100 spr-line-height-100',
|
|
46
18
|
]"
|
|
47
19
|
@click="() => action()"
|
|
48
20
|
>
|
|
@@ -52,16 +24,17 @@
|
|
|
52
24
|
</template>
|
|
53
25
|
|
|
54
26
|
<script lang="ts" setup>
|
|
27
|
+
import { Icon } from '@iconify/vue';
|
|
28
|
+
|
|
55
29
|
import { snackEmitTypes, snackPropTypes } from './snack';
|
|
56
30
|
import { useSnack } from './use-snack';
|
|
57
|
-
import { Icon } from '@iconify/vue';
|
|
58
31
|
|
|
59
|
-
const iconSize =
|
|
32
|
+
const iconSize = '24px';
|
|
60
33
|
|
|
61
34
|
const props = defineProps(snackPropTypes);
|
|
62
35
|
const emit = defineEmits(snackEmitTypes);
|
|
63
|
-
const { snackRef, snackProps, snackToneCssClass, snackIcon, handleClick } = useSnack(props, emit);
|
|
64
36
|
|
|
37
|
+
const { snackRef, snackProps, snackToneCssClass, snackIcon, handleClick } = useSnack(props, emit);
|
|
65
38
|
</script>
|
|
66
39
|
|
|
67
40
|
<style lang="scss" scoped>
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { computed, ref, ComputedRef } from 'vue';
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
+
|
|
4
5
|
import type { SetupContext } from 'vue';
|
|
5
6
|
import type { SnackEmitTypes, SnackPropTypes } from './snack';
|
|
6
7
|
|
|
7
8
|
export const useSnack = (props: SnackPropTypes, emit: SetupContext<SnackEmitTypes>['emit']) => {
|
|
8
9
|
const snackRef = ref<HTMLButtonElement | null>(null);
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
const { text, actionText, tone, showAction, showIcon, duration } = props;
|
|
11
12
|
|
|
12
13
|
const snackProps: ComputedRef<Record<string, unknown>> = computed(() => {
|
|
@@ -22,18 +23,18 @@ export const useSnack = (props: SnackPropTypes, emit: SetupContext<SnackEmitType
|
|
|
22
23
|
|
|
23
24
|
const snackToneCssClass: ComputedRef<string> = computed(() => {
|
|
24
25
|
return classNames({
|
|
25
|
-
'text-kangkong-500': tone === 'success',
|
|
26
|
-
'text-tomato-500': tone === 'caution',
|
|
27
|
-
'text-carrot-500': tone === 'danger',
|
|
28
|
-
'text-blueberry-500': tone === 'information',
|
|
26
|
+
'spr-text-kangkong-500': tone === 'success',
|
|
27
|
+
'spr-text-tomato-500': tone === 'caution',
|
|
28
|
+
'spr-text-carrot-500': tone === 'danger',
|
|
29
|
+
'spr-text-blueberry-500': tone === 'information',
|
|
29
30
|
});
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
const snackIcon = computed(() => {
|
|
33
|
-
if (props.tone ==
|
|
34
|
-
else if (props.tone ==
|
|
35
|
-
else if (props.tone ==
|
|
36
|
-
return
|
|
34
|
+
if (props.tone == 'caution') return 'ph:warning-fill';
|
|
35
|
+
else if (props.tone == 'danger') return 'ph:warning-circle-fill';
|
|
36
|
+
else if (props.tone == 'success') return 'ph:check-circle-fill';
|
|
37
|
+
return 'ph:info-fill';
|
|
37
38
|
});
|
|
38
39
|
|
|
39
40
|
const handleClick = (evt: MouseEvent) => {
|
|
@@ -45,6 +46,6 @@ export const useSnack = (props: SnackPropTypes, emit: SetupContext<SnackEmitType
|
|
|
45
46
|
snackProps,
|
|
46
47
|
snackToneCssClass,
|
|
47
48
|
snackIcon,
|
|
48
|
-
handleClick
|
|
49
|
+
handleClick,
|
|
49
50
|
};
|
|
50
51
|
};
|
|
@@ -3,12 +3,8 @@
|
|
|
3
3
|
<Transition name="snackbar">
|
|
4
4
|
<div v-if="snackbarStore.snacks.length > 0" class="snackbar-wrapper slide-in-element">
|
|
5
5
|
<TransitionGroup name="snackbar" tag="ul">
|
|
6
|
-
<li
|
|
7
|
-
|
|
8
|
-
:key="snack.id"
|
|
9
|
-
class="snackbar-snack mb-size-spacing-3xs"
|
|
10
|
-
>
|
|
11
|
-
<spr-snack
|
|
6
|
+
<li v-for="snack in snackbarStore.snacks" :key="snack.id" class="snackbar-snack spr-mb-size-spacing-3xs">
|
|
7
|
+
<spr-snack
|
|
12
8
|
:text="snack.text"
|
|
13
9
|
:action-text="snack.actionText"
|
|
14
10
|
:tone="snack.tone"
|
|
@@ -26,21 +22,23 @@
|
|
|
26
22
|
|
|
27
23
|
<script lang="ts" setup>
|
|
28
24
|
import { useSnackbarStore } from '@/stores/useSnackbarStore';
|
|
25
|
+
|
|
29
26
|
import SprSnack from '@/components/snackbar/snack/snack.vue';
|
|
30
27
|
import { useSnackbar } from '@/components/snackbar/use-snackbar';
|
|
28
|
+
|
|
31
29
|
import { SnackPropTypes } from './snack/snack';
|
|
32
|
-
import { createPinia } from 'pinia'
|
|
30
|
+
import { createPinia } from 'pinia';
|
|
33
31
|
|
|
34
32
|
const pinia = createPinia();
|
|
35
33
|
const snackbarStore = useSnackbarStore(pinia);
|
|
36
34
|
const { showSnackbar, showSuccess, showInformation, showDanger, showCaution } = useSnackbar();
|
|
37
35
|
|
|
38
|
-
defineExpose({
|
|
39
|
-
showSnackbar: (payload: SnackPropTypes) => showSnackbar(payload),
|
|
40
|
-
showSuccess: (payload: SnackPropTypes) => showSuccess(payload),
|
|
41
|
-
showInformation: (payload: SnackPropTypes) => showInformation(payload),
|
|
42
|
-
showDanger: (payload: SnackPropTypes) => showDanger(payload),
|
|
43
|
-
showCaution: (payload: SnackPropTypes) => showCaution(payload)
|
|
36
|
+
defineExpose({
|
|
37
|
+
showSnackbar: (payload: SnackPropTypes) => showSnackbar(payload),
|
|
38
|
+
showSuccess: (payload: SnackPropTypes) => showSuccess(payload),
|
|
39
|
+
showInformation: (payload: SnackPropTypes) => showInformation(payload),
|
|
40
|
+
showDanger: (payload: SnackPropTypes) => showDanger(payload),
|
|
41
|
+
showCaution: (payload: SnackPropTypes) => showCaution(payload),
|
|
44
42
|
});
|
|
45
43
|
</script>
|
|
46
44
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-bind="switchProps" :class="['flex items-center gap-2', switchTextClass]">
|
|
2
|
+
<div v-bind="switchProps" :class="['spr-flex spr-items-center spr-gap-2', switchTextClass]">
|
|
3
3
|
<label>
|
|
4
4
|
<slot name="leftText">
|
|
5
5
|
<slot></slot>
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<div
|
|
9
9
|
ref="switchWrapperRef"
|
|
10
10
|
:class="{
|
|
11
|
-
'relative flex items-center': true,
|
|
12
|
-
'cursor-pointer transition duration-300 ease-in-out active:scale-90': !props.disabled,
|
|
11
|
+
'spr-relative spr-flex spr-items-center': true,
|
|
12
|
+
'spr-cursor-pointer spr-transition spr-duration-300 spr-ease-in-out active:spr-scale-90': !props.disabled,
|
|
13
13
|
}"
|
|
14
14
|
>
|
|
15
15
|
<input
|
|
@@ -17,11 +17,17 @@
|
|
|
17
17
|
v-model="proxyValue"
|
|
18
18
|
type="checkbox"
|
|
19
19
|
name="checkbox"
|
|
20
|
-
:class="[
|
|
20
|
+
:class="[
|
|
21
|
+
'input spr-absolute spr-left-0 spr-top-0 spr-z-10 spr-m-0 spr-h-6 spr-w-12 spr-opacity-0',
|
|
22
|
+
switchInputClass,
|
|
23
|
+
]"
|
|
21
24
|
:disabled="props.disabled"
|
|
22
25
|
/>
|
|
23
26
|
<span
|
|
24
|
-
:class="[
|
|
27
|
+
:class="[
|
|
28
|
+
'switch-mark spr-relative spr-box-border spr-inline-block spr-h-6 spr-w-12 spr-rounded-[40px] spr-p-1',
|
|
29
|
+
switchMarkClass,
|
|
30
|
+
]"
|
|
25
31
|
></span>
|
|
26
32
|
</div>
|
|
27
33
|
<label>
|
|
@@ -48,19 +54,19 @@ const { switchWrapperRef, switchRef, switchProps, switchMarkClass, switchTextCla
|
|
|
48
54
|
<style lang="scss" scoped>
|
|
49
55
|
.input {
|
|
50
56
|
&:checked ~ .switch-mark:before {
|
|
51
|
-
@apply left-[1.7rem];
|
|
57
|
+
@apply spr-left-[1.7rem];
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
.switch-mark {
|
|
56
62
|
&:before,
|
|
57
63
|
&:after {
|
|
58
|
-
@apply absolute;
|
|
64
|
+
@apply spr-absolute;
|
|
59
65
|
content: '';
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
&:before {
|
|
63
|
-
@apply left-1 top-1 h-4 w-4 rounded-[50%] bg-white-50;
|
|
69
|
+
@apply spr-left-1 spr-top-1 spr-h-4 spr-w-4 spr-rounded-[50%] spr-bg-white-50;
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
</style>
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { computed, ref, ComputedRef, toRefs } from 'vue';
|
|
2
2
|
import { useElementHover, useMousePressed } from '@vueuse/core';
|
|
3
|
+
|
|
3
4
|
import classNames from 'classnames';
|
|
5
|
+
|
|
4
6
|
import type { SwitchPropTypes } from './switch';
|
|
5
7
|
|
|
6
8
|
export const useSwitch = (props: SwitchPropTypes) => {
|
|
7
9
|
const switchWrapperRef = ref<HTMLDivElement | null>(null);
|
|
8
10
|
const switchRef = ref<HTMLInputElement | null>(null);
|
|
11
|
+
|
|
9
12
|
const isHovered = useElementHover(switchWrapperRef);
|
|
10
13
|
const { pressed } = useMousePressed({ target: switchRef });
|
|
11
14
|
const { disabled, state, modelValue } = toRefs(props);
|
|
@@ -37,43 +40,41 @@ export const useSwitch = (props: SwitchPropTypes) => {
|
|
|
37
40
|
});
|
|
38
41
|
|
|
39
42
|
function getDefaultBackground(): string {
|
|
40
|
-
return props.modelValue ? 'background-color-success-base' : 'switch-background-default';
|
|
43
|
+
return props.modelValue ? 'spr-background-color-success-base' : 'spr-switch-background-default';
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
function getHoveredBackground(): string {
|
|
44
|
-
return props.modelValue ? 'background-color-success-hover' : 'switch-background-hover';
|
|
47
|
+
return props.modelValue ? 'spr-background-color-success-hover' : 'spr-switch-background-hover';
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function getPressedBackground(): string {
|
|
48
|
-
return props.modelValue ? 'background-color-success-pressed' : 'switch-background-pressed';
|
|
51
|
+
return props.modelValue ? 'spr-background-color-success-pressed' : 'spr-switch-background-pressed';
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
function getDisabledBackground(): string {
|
|
52
55
|
return classNames(
|
|
53
56
|
{
|
|
54
|
-
'background-color-success-base': props.modelValue,
|
|
55
|
-
'switch-background-default': !props.modelValue,
|
|
57
|
+
'spr-background-color-success-base': props.modelValue,
|
|
58
|
+
'spr-switch-background-default': !props.modelValue,
|
|
56
59
|
},
|
|
57
|
-
'opacity-60',
|
|
60
|
+
'spr-opacity-60',
|
|
58
61
|
);
|
|
59
62
|
}
|
|
60
63
|
// #endregion - Background CSS Class
|
|
61
64
|
|
|
62
65
|
const switchTextClass: ComputedRef<string> = computed(() => {
|
|
63
66
|
if (props.disabled) {
|
|
64
|
-
return 'text-color-disabled';
|
|
67
|
+
return 'spr-text-color-disabled';
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
return 'text-color-strong';
|
|
70
|
+
return 'spr-text-color-strong';
|
|
68
71
|
});
|
|
69
72
|
|
|
70
73
|
const switchAnimationCssClass: ComputedRef<string> = computed(() => {
|
|
71
74
|
return classNames(
|
|
72
|
-
'transition-colors',
|
|
73
|
-
'before:transition-all',
|
|
74
|
-
'
|
|
75
|
-
'after:transition-all',
|
|
76
|
-
'after:duration-150',
|
|
75
|
+
'spr-transition-colors',
|
|
76
|
+
'before:spr-transition-all before:spr-duration-150',
|
|
77
|
+
'after:spr-transition-all after:spr-duration-150',
|
|
77
78
|
);
|
|
78
79
|
});
|
|
79
80
|
|
|
@@ -83,8 +84,8 @@ export const useSwitch = (props: SwitchPropTypes) => {
|
|
|
83
84
|
|
|
84
85
|
const switchInputClass: ComputedRef<string> = computed(() => {
|
|
85
86
|
return classNames({
|
|
86
|
-
'cursor-not-allowed': props.disabled,
|
|
87
|
-
'cursor-pointer': !props.disabled,
|
|
87
|
+
'spr-cursor-not-allowed': props.disabled,
|
|
88
|
+
'spr-cursor-pointer': !props.disabled,
|
|
88
89
|
});
|
|
89
90
|
});
|
|
90
91
|
|
|
@@ -1,52 +1,67 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="spr-border-color-weak spr-w-full spr-rounded-border-radius-lg spr-border spr-border-solid spr-overflow-hidden"
|
|
4
|
+
>
|
|
5
|
+
<div v-if="!!$slots.default" :class="[{ 'spr-px-size-spacing-sm spr-py-size-spacing-xs': !!$slots.default }]">
|
|
4
6
|
<slot />
|
|
5
7
|
</div>
|
|
6
8
|
|
|
7
|
-
<div class="h-[400px] overflow-auto">
|
|
8
|
-
<table aria-describedby="describe" class="w-full table-fixed" cellspacing="0" cellpadding="0">
|
|
9
|
+
<div class="spr-h-[400px] spr-overflow-auto">
|
|
10
|
+
<table aria-describedby="describe" class="spr-w-full spr-table-fixed" cellspacing="0" cellpadding="0">
|
|
9
11
|
<thead>
|
|
10
12
|
<tr>
|
|
11
13
|
<th
|
|
12
14
|
v-for="(header, keyHeader) in headers"
|
|
13
15
|
:key="keyHeader"
|
|
14
16
|
:class="[
|
|
15
|
-
'
|
|
17
|
+
'spr-sticky spr-top-0 spr-z-10',
|
|
18
|
+
'spr-background-color-surface spr-min-h-12 spr-py-size-spacing-3xs spr-px-size-spacing-2xs',
|
|
19
|
+
'spr-text-color-strong spr-font-size-100 spr-font-line-height-100 spr-font-letter-spacing-normal spr-text-start spr-font-medium spr-uppercase',
|
|
20
|
+
'spr-border-x-0 spr-border-y spr-border-solid spr-border-color-weak',
|
|
16
21
|
{
|
|
17
|
-
'cursor-pointer': header.sort,
|
|
18
|
-
'
|
|
22
|
+
'spr-cursor-pointer': header.sort,
|
|
23
|
+
'spr-border-t-0': !$slots.default,
|
|
19
24
|
},
|
|
20
25
|
]"
|
|
21
26
|
@click="header.sort && sortData(header.field)"
|
|
22
27
|
>
|
|
23
|
-
<div class="flex flex-row items-center gap-size-spacing-5xs">
|
|
28
|
+
<div class="spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-5xs">
|
|
24
29
|
<span>{{ header.name }}</span>
|
|
25
|
-
<span v-if="header.sort" class="flex flex-row items-center"
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
<span v-if="header.sort" class="spr-flex spr-flex-row spr-items-center">
|
|
31
|
+
<Icon icon="ph:caret-up-down-light" />
|
|
32
|
+
</span>
|
|
28
33
|
</div>
|
|
29
34
|
</th>
|
|
35
|
+
|
|
30
36
|
<!-- for action Button -->
|
|
31
37
|
<th
|
|
32
38
|
v-if="action"
|
|
33
|
-
class="
|
|
39
|
+
:class="[
|
|
40
|
+
'spr-sticky spr-top-0 spr-z-10',
|
|
41
|
+
'spr-background-color-surface spr-min-h-12 spr-py-size-spacing-3xs',
|
|
42
|
+
'spr-text-color-strong spr-text-start spr-font-medium spr-uppercase spr-font-size-100 spr-font-line-height-100 spr-font-letter-spacing-normal',
|
|
43
|
+
'spr-border-y spr-border-solid spr-border-color-weak spr-border-x-0',
|
|
44
|
+
]"
|
|
34
45
|
>
|
|
35
46
|
<slot
|
|
36
47
|
name="action-name"
|
|
37
|
-
class="background-color-surface text-color-strong font-size-100 font-line-height-100 font-letter-spacing-normal uppercase"
|
|
48
|
+
class="spr-background-color-surface spr-text-color-strong spr-font-size-100 spr-font-line-height-100 spr-font-letter-spacing-normal spr-uppercase"
|
|
38
49
|
/>
|
|
39
50
|
</th>
|
|
40
51
|
</tr>
|
|
41
52
|
</thead>
|
|
42
|
-
<tbody v-if="sortedData.length > 0">
|
|
43
|
-
<tr
|
|
53
|
+
<tbody v-if="sortedData.length > 0 && !loading">
|
|
54
|
+
<tr
|
|
55
|
+
v-for="(item, keyIndex) in sortedData"
|
|
56
|
+
:key="keyIndex"
|
|
57
|
+
class="hover:spr-background-color-hover spr-min-h-[60px]"
|
|
58
|
+
>
|
|
44
59
|
<td
|
|
45
60
|
v-for="(column, headerKey) in headers"
|
|
46
61
|
:key="headerKey"
|
|
47
|
-
class="border-color-weak overflow-hidden border-x-0 border-b border-t-0 border-solid
|
|
62
|
+
class="spr-border-color-weak spr-overflow-hidden spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid spr-p-3"
|
|
48
63
|
>
|
|
49
|
-
<div v-if="sortedData[keyIndex][column.field]" class="flex flex-row items-center gap-2">
|
|
64
|
+
<div v-if="sortedData[keyIndex][column.field]" class="spr-flex spr-flex-row spr-items-center spr-gap-2">
|
|
50
65
|
<spr-avatar
|
|
51
66
|
v-if="column.hasAvatar && sortedData[keyIndex][column.field].image"
|
|
52
67
|
size="lg"
|
|
@@ -56,31 +71,40 @@
|
|
|
56
71
|
<div>
|
|
57
72
|
<div
|
|
58
73
|
:class="[
|
|
59
|
-
'text-color-strong font-size-200 font-normal',
|
|
60
|
-
{ 'text-color-strong body-sm-regular-medium': column.hasSubtext },
|
|
74
|
+
'spr-text-color-strong spr-font-size-200 spr-font-normal',
|
|
75
|
+
{ 'spr-text-color-strong spr-body-sm-regular-medium': column.hasSubtext },
|
|
61
76
|
]"
|
|
62
77
|
>
|
|
63
78
|
{{ sortedData[keyIndex][column.field].title }}
|
|
64
79
|
</div>
|
|
65
|
-
<div v-if="column.hasSubtext" class="text-color-base text-xs font-normal">
|
|
80
|
+
<div v-if="column.hasSubtext" class="spr-text-color-base spr-text-xs spr-font-normal">
|
|
66
81
|
{{ sortedData[keyIndex][column.field].subtext }}
|
|
67
82
|
</div>
|
|
68
83
|
</div>
|
|
69
84
|
</div>
|
|
70
85
|
</td>
|
|
71
|
-
<td
|
|
72
|
-
|
|
86
|
+
<td
|
|
87
|
+
v-if="action"
|
|
88
|
+
class="spr-border-color-weak spr-overflow-hidden spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid"
|
|
89
|
+
>
|
|
90
|
+
<div class="spr-flex spr-items-center">
|
|
73
91
|
<slot name="action" :row="item" />
|
|
74
92
|
</div>
|
|
75
93
|
</td>
|
|
76
94
|
</tr>
|
|
77
95
|
</tbody>
|
|
78
96
|
<tbody v-else>
|
|
79
|
-
<tr>
|
|
80
|
-
<td :colspan="getHeaderCount" class="overflow-hidden">
|
|
97
|
+
<tr v-if="!loading">
|
|
98
|
+
<td :colspan="getHeaderCount" class="spr-overflow-hidden">
|
|
81
99
|
<SprEmptyState />
|
|
82
100
|
</td>
|
|
83
101
|
</tr>
|
|
102
|
+
<tr v-else>
|
|
103
|
+
<td :colspan="getHeaderCount" class="spr-overflow-hidden">
|
|
104
|
+
<div v-if="!$slots.loading" class="spr-flex spr-justify-center spr-items-center">Loading...</div>
|
|
105
|
+
<slot name="loading" class="" />
|
|
106
|
+
</td>
|
|
107
|
+
</tr>
|
|
84
108
|
</tbody>
|
|
85
109
|
</table>
|
|
86
110
|
</div>
|
|
@@ -89,11 +113,14 @@
|
|
|
89
113
|
|
|
90
114
|
<script setup lang="ts">
|
|
91
115
|
import { Icon } from '@iconify/vue';
|
|
116
|
+
|
|
92
117
|
import SprAvatar from '@/components/avatar/avatar.vue';
|
|
93
118
|
import SprEmptyState from '@/components/empty-state/empty-state.vue';
|
|
119
|
+
|
|
94
120
|
import { tablePropTypes } from './table';
|
|
95
121
|
import { useTable } from './use-table';
|
|
96
122
|
|
|
97
123
|
const props = defineProps(tablePropTypes);
|
|
124
|
+
|
|
98
125
|
const { sortedData, sortData, getHeaderCount } = useTable(props);
|
|
99
126
|
</script>
|