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.
Files changed (45) hide show
  1. package/dist/design-system-next.js +2937 -2937
  2. package/dist/design-system-next.js.gz +0 -0
  3. package/dist/main.css +1 -1
  4. package/dist/main.css.gz +0 -0
  5. package/dist/main.d.ts +1 -6
  6. package/package.json +1 -1
  7. package/src/App.vue +8 -8
  8. package/src/assets/styles/tailwind.css +366 -367
  9. package/src/components/avatar/avatar.vue +1 -1
  10. package/src/components/avatar/use-avatar.ts +40 -34
  11. package/src/components/badge/badge.vue +2 -2
  12. package/src/components/badge/use-badge.ts +16 -16
  13. package/src/components/button/use-button.ts +39 -34
  14. package/src/components/checkbox/checkbox.vue +2 -2
  15. package/src/components/checkbox/use-checkbox.ts +23 -20
  16. package/src/components/dropdown/dropdown.vue +6 -6
  17. package/src/components/dropdown/use-dropdown.ts +6 -5
  18. package/src/components/empty-state/empty-state.vue +6 -5
  19. package/src/components/empty-state/use-empty-state.ts +8 -6
  20. package/src/components/input/input.ts +6 -1
  21. package/src/components/input/input.vue +7 -4
  22. package/src/components/input/use-input.ts +49 -47
  23. package/src/components/lozenge/lozenge.vue +7 -5
  24. package/src/components/lozenge/use-lozenge.ts +23 -15
  25. package/src/components/modal/modal.vue +8 -8
  26. package/src/components/modal/use-modal.ts +8 -8
  27. package/src/components/radio/radio.vue +1 -1
  28. package/src/components/radio/use-radio.ts +15 -13
  29. package/src/components/sidenav/sidenav.vue +158 -138
  30. package/src/components/sidepanel/sidepanel.vue +29 -21
  31. package/src/components/sidepanel/use-sidepanel.ts +17 -15
  32. package/src/components/snackbar/snack/snack.vue +13 -40
  33. package/src/components/snackbar/snack/use-snack.ts +12 -11
  34. package/src/components/snackbar/snackbar.vue +11 -13
  35. package/src/components/switch/switch.vue +14 -8
  36. package/src/components/switch/use-switch.ts +16 -15
  37. package/src/components/table/table.ts +5 -0
  38. package/src/components/table/table.vue +51 -24
  39. package/src/components/table/use-table.ts +1 -0
  40. package/src/components/tabs/tabs.vue +23 -19
  41. package/src/components/tabs/use-tabs.ts +5 -4
  42. package/src/components/timePicker/timePicker.vue +10 -9
  43. package/src/components/timePicker/use-timePicker.ts +31 -45
  44. package/src/components/tooltip/tooltip.vue +1 -1
  45. 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
- :name="`sidepanel`"
9
- enter-active-class="transition-transform duration-[0.3s] ease-[ease-out]"
10
- leave-active-class="transition-transform duration-[0.3s] ease-[ease-out]"
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(100vh_-_32px)] bg-white-50 rounded-border-radius-xl fixed right-4 z-[30] min-h-[200px] flex flex-col top-1/2 translate-y-[-50%] drop-shadow'
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 v-if="!$slots.header" class="flex justify-between tw-min-h-12 p-4 border-mushroom-200 border-b border-solid border-0 subheading-xs text-color-strong">
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 v-if="$slots.footer" class="absolute bottom-0 left-0 w-full border-0 border-t border-mushroom-200 bg-white-50 border-solid py-3 rounded-b-border-radius-xl">
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 { sidepanelRef, sidepanelSizesClasses, sidepanelMidState, sidepanelStartEndState, handleClose, handleBackdropClick } = useSidepanel(props, emit);
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
- v-if="showIcon"
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 = "24px";
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
- // import { useElementHover} from '@vueuse/core';
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
- // const isHovered = useElementHover(snackRef);
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 == "caution") return "ph:warning-fill";
34
- else if (props.tone == "danger") return "ph:warning-circle-fill";
35
- else if (props.tone == "success") return "ph:check-circle-fill";
36
- return "ph:info-fill";
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
- v-for="snack in snackbarStore.snacks"
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="['input absolute left-0 top-0 z-10 m-0 h-6 w-12 opacity-0', switchInputClass]"
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="['switch-mark relative box-border inline-block h-6 w-12 rounded-[40px] p-1', switchMarkClass]"
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
- 'before:duration-150',
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
 
@@ -57,6 +57,11 @@ export const tablePropTypes = {
57
57
  size: 'large',
58
58
  }),
59
59
  },
60
+
61
+ loading: {
62
+ type: Boolean as PropType<boolean>,
63
+ default: false,
64
+ },
60
65
  };
61
66
 
62
67
  export type TablePropTypes = ExtractPropTypes<typeof tablePropTypes>;
@@ -1,52 +1,67 @@
1
1
  <template>
2
- <div class="border-color-weak w-full rounded-border-radius-lg border border-solid">
3
- <div v-if="!!$slots.default" :class="[{ 'px-size-spacing-sm py-size-spacing-xs': !!$slots.default }]">
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
- 'background-color-surface text-color-strong font-size-100 font-line-height-100 font-letter-spacing-normal background-color-surface border-color-weak sticky top-0 z-10 min-h-12 border-x-0 border-y border-solid px-size-spacing-2xs py-size-spacing-3xs text-start font-medium uppercase',
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
- 'first:rounded-tl-border-radius-lg last:rounded-tr-border-radius-lg': !$slots.default,
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
- ><Icon icon="ph:caret-up-down-light"
27
- /></span>
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="background-color-surface text-color-strong font-size-100 font-line-height-100 font-letter-spacing-normal background-color-surface border-color-weak sticky top-0 z-10 min-h-12 border-x-0 border-y border-solid px-size-spacing-2xs py-size-spacing-3xs text-start font-medium uppercase"
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 v-for="(item, keyIndex) in sortedData" :key="keyIndex" class="hover:background-color-hover min-h-[60px]">
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 px-6 py-3"
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 v-if="action" class="border-color-weak overflow-hidden border-x-0 border-b border-t-0 border-solid">
72
- <div class="flex items-center space-x-1 px-6 py-3">
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>
@@ -1,4 +1,5 @@
1
1
  import { ref, computed, toRefs } from 'vue';
2
+
2
3
  import type { TablePropTypes } from './table';
3
4
 
4
5
  export const useTable = (props: TablePropTypes) => {