@sabrenski/spire-ui 0.0.5 → 0.0.7

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 (86) hide show
  1. package/dist/index.d.ts +170 -4
  2. package/dist/spire-ui.css +1 -1
  3. package/dist/spire-ui.es.js +7040 -6773
  4. package/dist/spire-ui.umd.js +10 -10
  5. package/package.json +83 -70
  6. package/src/components/Accordion/AccordionContent.vue +5 -2
  7. package/src/components/Accordion/AccordionItem.vue +4 -0
  8. package/src/components/Accordion/AccordionRoot.vue +4 -2
  9. package/src/components/Accordion/AccordionTrigger.vue +4 -1
  10. package/src/components/Avatar/Avatar.vue +4 -0
  11. package/src/components/Badge/Badge.vue +4 -0
  12. package/src/components/BadgeContainer/BadgeContainer.vue +4 -1
  13. package/src/components/Breadcrumb/BreadcrumbLink.vue +4 -1
  14. package/src/components/Breadcrumb/BreadcrumbRoot.vue +4 -1
  15. package/src/components/Button/Button.vue +5 -1
  16. package/src/components/Callout/Callout.vue +4 -0
  17. package/src/components/Card/Card.vue +5 -1
  18. package/src/components/Card/CardContent.vue +5 -1
  19. package/src/components/Card/CardFooter.vue +5 -1
  20. package/src/components/Card/CardHeader.vue +5 -1
  21. package/src/components/Card/CardImage.vue +4 -2
  22. package/src/components/Chart/BarChart.vue +4 -0
  23. package/src/components/Chart/BaseChart.vue +52 -47
  24. package/src/components/Chart/DonutChart.vue +4 -2
  25. package/src/components/Chart/LineChart.vue +4 -0
  26. package/src/components/Checkbox/Checkbox.test.ts +94 -0
  27. package/src/components/Checkbox/Checkbox.vue +170 -1
  28. package/src/components/ChoiceChip/ChoiceChip.vue +11 -5
  29. package/src/components/ChoiceChipGroup/ChoiceChipGroup.vue +4 -2
  30. package/src/components/ColorPicker/ColorArea.vue +4 -2
  31. package/src/components/ColorPicker/ColorPicker.vue +4 -2
  32. package/src/components/ColorPicker/ColorSlider.vue +5 -1
  33. package/src/components/Combobox/Combobox.vue +97 -91
  34. package/src/components/DataTable/DataTable.vue +5 -1
  35. package/src/components/DatePicker/DatePicker.vue +5 -1
  36. package/src/components/Drawer/Drawer.vue +13 -3
  37. package/src/components/Dropdown/Dropdown.vue +4 -2
  38. package/src/components/Dropdown/DropdownItem.vue +4 -0
  39. package/src/components/Dropdown/DropdownSubTrigger.vue +4 -2
  40. package/src/components/EmptyState/EmptyState.vue +5 -1
  41. package/src/components/FileUpload/FileUpload.vue +12 -6
  42. package/src/components/Heading/Heading.vue +4 -0
  43. package/src/components/Icon/Icon.vue +5 -2
  44. package/src/components/Input/Input.vue +5 -1
  45. package/src/components/Layout/Container.vue +4 -0
  46. package/src/components/Layout/Grid.vue +4 -1
  47. package/src/components/Layout/GridItem.vue +4 -1
  48. package/src/components/Layout/Stack.vue +4 -0
  49. package/src/components/Modal/Modal.test.ts +68 -13
  50. package/src/components/Modal/Modal.vue +94 -91
  51. package/src/components/Pagination/Pagination.vue +5 -1
  52. package/src/components/Popover/Popover.vue +4 -1
  53. package/src/components/Progress/Progress.vue +5 -0
  54. package/src/components/Radio/Radio.test.ts +88 -0
  55. package/src/components/Radio/Radio.vue +169 -1
  56. package/src/components/Rating/Rating.vue +5 -1
  57. package/src/components/SegmentedControl/SegmentedControl.vue +5 -1
  58. package/src/components/Select/Select.vue +61 -55
  59. package/src/components/Sidebar/SidebarGroup.vue +4 -0
  60. package/src/components/Sidebar/SidebarItem.vue +4 -0
  61. package/src/components/Sidebar/SidebarLayout.vue +5 -2
  62. package/src/components/Sidebar/SidebarRoot.vue +4 -2
  63. package/src/components/Skeleton/Skeleton.vue +5 -1
  64. package/src/components/Slider/Slider.vue +5 -1
  65. package/src/components/Spinner/Spinner.vue +4 -1
  66. package/src/components/SpireProvider/SpireProvider.vue +4 -1
  67. package/src/components/Stepper/StepperItem.vue +4 -0
  68. package/src/components/Stepper/StepperRoot.vue +4 -2
  69. package/src/components/Stepper/StepperTrigger.vue +6 -2
  70. package/src/components/Switch/Switch.vue +5 -1
  71. package/src/components/Tabs/Tabs.vue +4 -1
  72. package/src/components/Text/Text.vue +4 -0
  73. package/src/components/Textarea/Textarea.vue +13 -7
  74. package/src/components/TimePicker/TimePicker.vue +5 -1
  75. package/src/components/Timeline/Timeline.vue +4 -0
  76. package/src/components/Timeline/TimelineItem.vue +4 -0
  77. package/src/components/Toast/ToastItem.vue +5 -1
  78. package/src/components/Toast/ToastProvider.vue +5 -3
  79. package/src/components/ToggleButton/ToggleButton.vue +5 -1
  80. package/src/components/ToggleGroup/ToggleGroup.vue +5 -1
  81. package/src/components/Tooltip/Tooltip.vue +9 -1
  82. package/src/components/TreeView/TreeView.vue +4 -1
  83. package/src/components/TreeView/TreeViewItem.vue +4 -0
  84. package/src/index.ts +3 -0
  85. package/src/styles/main.css +21 -21
  86. package/src/types/common.ts +4 -0
@@ -2,6 +2,7 @@
2
2
  import { ref, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
3
3
  import Icon from '../Icon/Icon.vue'
4
4
  import type { IconInput } from '../Icon/Icon.vue'
5
+ import type { ClassValue } from '../../types/common'
5
6
 
6
7
  export interface TabItem {
7
8
  /** Display label */
@@ -15,6 +16,8 @@ export interface TabItem {
15
16
  }
16
17
 
17
18
  export interface TabsProps {
19
+ /** Additional CSS classes */
20
+ class?: ClassValue
18
21
  /** Currently active tab value (v-model) */
19
22
  modelValue: string | number
20
23
  /** Tab items configuration */
@@ -136,7 +139,7 @@ onBeforeUnmount(() => {
136
139
  </script>
137
140
 
138
141
  <template>
139
- <div class="ui-tabs">
142
+ <div :class="[props.class, 'ui-tabs']">
140
143
  <div
141
144
  ref="tabListRef"
142
145
  class="ui-tabs__list"
@@ -1,11 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, type CSSProperties } from 'vue'
3
+ import type { ClassValue } from '../../types/common'
3
4
 
4
5
  type TextTag = 'p' | 'span' | 'div' | 'label' | 'li'
5
6
  type TextSize = 'xs' | 'sm' | 'base' | 'md' | 'lg' | 'xl'
6
7
  type TextWeight = 'regular' | 'medium' | 'semibold' | 'bold'
7
8
 
8
9
  export interface TextProps {
10
+ /** Additional CSS classes */
11
+ class?: ClassValue
9
12
  /** Semantic HTML tag */
10
13
  as?: TextTag
11
14
  /** Font size */
@@ -30,6 +33,7 @@ const props = withDefaults(defineProps<TextProps>(), {
30
33
  })
31
34
 
32
35
  const classes = computed(() => [
36
+ props.class,
33
37
  'ui-text',
34
38
  `ui-text--${props.size}`,
35
39
  `ui-text--${props.weight}`,
@@ -3,8 +3,11 @@ import { ref, computed, watch, onMounted, nextTick, useSlots } from 'vue'
3
3
  import Icon from '../Icon/Icon.vue'
4
4
  import type { IconInput } from '../Icon/Icon.vue'
5
5
  import { useId } from '../../composables'
6
+ import type { ClassValue } from '../../types/common'
6
7
 
7
8
  export interface TextareaProps {
9
+ /** Additional CSS classes */
10
+ class?: ClassValue
8
11
  /** Textarea value (v-model) */
9
12
  modelValue?: string
10
13
  /** Label text above the textarea */
@@ -153,13 +156,16 @@ defineExpose({
153
156
 
154
157
  <template>
155
158
  <div
156
- class="ui-textarea-field"
157
- :class="{
158
- 'ui-textarea-field--block': block,
159
- 'ui-textarea-field--disabled': disabled,
160
- 'ui-textarea-field--error': error || isOverLimit,
161
- 'ui-textarea-field--readonly': readonly
162
- }"
159
+ :class="[
160
+ props.class,
161
+ 'ui-textarea-field',
162
+ {
163
+ 'ui-textarea-field--block': block,
164
+ 'ui-textarea-field--disabled': disabled,
165
+ 'ui-textarea-field--error': error || isOverLimit,
166
+ 'ui-textarea-field--readonly': readonly
167
+ }
168
+ ]"
163
169
  >
164
170
  <label
165
171
  v-if="label"
@@ -21,8 +21,11 @@ import {
21
21
  isValidTimeString,
22
22
  type TimeFormat
23
23
  } from '../../utils/time'
24
+ import type { ClassValue } from '../../types/common'
24
25
 
25
26
  export interface TimePickerProps {
27
+ /** Additional CSS classes */
28
+ class?: ClassValue
26
29
  /** Selected time in HH:mm format (24h) */
27
30
  modelValue?: string
28
31
  /** Time display format */
@@ -342,8 +345,9 @@ defineExpose({
342
345
 
343
346
  <template>
344
347
  <div
345
- class="ui-timepicker"
346
348
  :class="[
349
+ props.class,
350
+ 'ui-timepicker',
347
351
  `ui-timepicker--${size}`,
348
352
  {
349
353
  'ui-timepicker--block': block,
@@ -2,8 +2,11 @@
2
2
  import { provide, toRef, ref, computed, onBeforeUpdate } from 'vue'
3
3
  import { TimelineKey } from './keys'
4
4
  import type { TimelineAlign, TimelineLineStyle } from './keys'
5
+ import type { ClassValue } from '../../types/common'
5
6
 
6
7
  export interface TimelineProps {
8
+ /** Additional CSS classes */
9
+ class?: ClassValue
7
10
  /** Content alignment relative to the spine */
8
11
  align?: TimelineAlign
9
12
  /** Connector line style */
@@ -36,6 +39,7 @@ provide(TimelineKey, {
36
39
  })
37
40
 
38
41
  const timelineClasses = computed(() => [
42
+ props.class,
39
43
  'ui-timeline',
40
44
  `ui-timeline--${props.align}`,
41
45
  `ui-timeline--line-${props.lineStyle}`
@@ -2,8 +2,11 @@
2
2
  import { computed, inject, onMounted, ref, type Component } from 'vue'
3
3
  import { TimelineKey } from './keys'
4
4
  import type { TimelineDotColor } from './keys'
5
+ import type { ClassValue } from '../../types/common'
5
6
 
6
7
  export interface TimelineItemProps {
8
+ /** Additional CSS classes */
9
+ class?: ClassValue
7
10
  /** Dot color variant */
8
11
  dotColor?: TimelineDotColor
9
12
  /** Custom icon in dot */
@@ -37,6 +40,7 @@ const position = computed(() => {
37
40
  })
38
41
 
39
42
  const itemClasses = computed(() => [
43
+ props.class,
40
44
  'ui-timeline__item',
41
45
  `ui-timeline__item--${timeline.align.value}`,
42
46
  `ui-timeline__item--position-${position.value}`,
@@ -2,8 +2,11 @@
2
2
  import { ref, computed, onMounted, onUnmounted } from 'vue'
3
3
  import Avatar from '../Avatar/Avatar.vue'
4
4
  import type { Toast, ToastVariant } from './toastState'
5
+ import type { ClassValue } from '../../types/common'
5
6
 
6
7
  export interface ToastItemProps {
8
+ /** Additional CSS classes */
9
+ class?: ClassValue
7
10
  /** Toast data */
8
11
  toast: Toast
9
12
  }
@@ -100,8 +103,9 @@ const iconPaths: Record<ToastVariant, string> = {
100
103
 
101
104
  <template>
102
105
  <div
103
- class="ui-toast"
104
106
  :class="[
107
+ props.class,
108
+ 'ui-toast',
105
109
  `ui-toast--${toast.variant}`,
106
110
  { 'ui-toast--clickable': isClickable }
107
111
  ]"
@@ -1,13 +1,16 @@
1
1
  <script setup lang="ts">
2
2
  import { toasts, toastActions } from './toastState'
3
3
  import ToastItem from './ToastItem.vue'
4
+ import type { ClassValue } from '../../types/common'
4
5
 
5
6
  export interface ToastProviderProps {
7
+ /** Additional CSS classes */
8
+ class?: ClassValue
6
9
  /** Position of the toast container */
7
10
  position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center'
8
11
  }
9
12
 
10
- withDefaults(defineProps<ToastProviderProps>(), {
13
+ const props = withDefaults(defineProps<ToastProviderProps>(), {
11
14
  position: 'top-right'
12
15
  })
13
16
 
@@ -19,8 +22,7 @@ function handleDismiss(id: string) {
19
22
  <template>
20
23
  <Teleport to="body">
21
24
  <div
22
- class="ui-toast-provider"
23
- :class="[`ui-toast-provider--${position}`]"
25
+ :class="[props.class, 'ui-toast-provider', `ui-toast-provider--${position}`]"
24
26
  role="region"
25
27
  aria-label="Notifications"
26
28
  >
@@ -2,8 +2,11 @@
2
2
  import { computed, inject } from 'vue'
3
3
  import Icon from '../Icon/Icon.vue'
4
4
  import type { IconInput } from '../Icon/Icon.vue'
5
+ import type { ClassValue } from '../../types/common'
5
6
 
6
7
  export interface ToggleButtonProps {
8
+ /** Additional CSS classes */
9
+ class?: ClassValue
7
10
  /** Pressed state for standalone usage (v-model) */
8
11
  modelValue?: boolean
9
12
  /** Value for group usage */
@@ -74,8 +77,9 @@ const isIconOnly = computed(() => props.icon && !props.label)
74
77
  :aria-pressed="isPressed"
75
78
  :aria-label="isIconOnly ? label : undefined"
76
79
  :disabled="effectiveDisabled"
77
- class="ui-toggle-button"
78
80
  :class="[
81
+ props.class,
82
+ 'ui-toggle-button',
79
83
  `ui-toggle-button--${effectiveSize}`,
80
84
  {
81
85
  'ui-toggle-button--pressed': isPressed,
@@ -1,7 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { provide, computed, toRef } from 'vue'
3
+ import type { ClassValue } from '../../types/common'
3
4
 
4
5
  export interface ToggleGroupProps {
6
+ /** Additional CSS classes */
7
+ class?: ClassValue
5
8
  /** Selected value(s) - single value for 'single' type, array for 'multiple' */
6
9
  modelValue?: string | number | (string | number)[]
7
10
  /** Selection type */
@@ -60,8 +63,9 @@ const role = computed(() => props.type === 'single' ? 'radiogroup' : 'group')
60
63
  :aria-label="label"
61
64
  :aria-orientation="orientation"
62
65
  :aria-disabled="disabled || undefined"
63
- class="ui-toggle-group"
64
66
  :class="[
67
+ props.class,
68
+ 'ui-toggle-group',
65
69
  `ui-toggle-group--${orientation}`,
66
70
  { 'ui-toggle-group--disabled': disabled }
67
71
  ]"
@@ -1,8 +1,11 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, computed, onUnmounted } from 'vue'
3
3
  import { useId, useRelativePosition, calculatePosition, type Placement } from '../../composables'
4
+ import type { ClassValue } from '../../types/common'
4
5
 
5
6
  export interface TooltipProps {
7
+ /** Additional CSS classes */
8
+ class?: ClassValue
6
9
  /** Tooltip text content */
7
10
  text: string
8
11
  /** Preferred placement (may flip on collision) */
@@ -90,12 +93,17 @@ onUnmounted(() => {
90
93
  if (showTimer) clearTimeout(showTimer)
91
94
  if (hideTimer) clearTimeout(hideTimer)
92
95
  })
96
+
97
+ defineOptions({
98
+ inheritAttrs: false
99
+ })
93
100
  </script>
94
101
 
95
102
  <template>
96
103
  <span
97
104
  ref="triggerRef"
98
- class="ui-tooltip-trigger"
105
+ :class="[props.class, 'ui-tooltip-trigger']"
106
+ v-bind="$attrs"
99
107
  :aria-describedby="isVisible ? tooltipId : undefined"
100
108
  @mouseenter="show"
101
109
  @mouseleave="hide"
@@ -2,8 +2,11 @@
2
2
  import { provide, ref, computed, watch, toRef } from 'vue'
3
3
  import { TreeViewKey, TreeViewItemKey } from './keys'
4
4
  import type { TreeNode } from './keys'
5
+ import type { ClassValue } from '../../types/common'
5
6
 
6
7
  export interface TreeViewProps {
8
+ /** Additional CSS classes */
9
+ class?: ClassValue
7
10
  /** Hierarchical data */
8
11
  data: TreeNode[]
9
12
  /** Selected node ID(s) - v-model */
@@ -226,7 +229,7 @@ provide(TreeViewItemKey, {
226
229
 
227
230
  <template>
228
231
  <div
229
- class="ui-treeview"
232
+ :class="[props.class, 'ui-treeview']"
230
233
  role="tree"
231
234
  :aria-multiselectable="multiSelect"
232
235
  tabindex="0"
@@ -3,8 +3,11 @@ import { computed, inject, provide, ref, watch, onMounted, Transition, type Comp
3
3
  import { TreeViewKey, TreeViewItemKey } from './keys'
4
4
  import type { TreeNode } from './keys'
5
5
  import { useInternalIcon } from '../../config/icons'
6
+ import type { ClassValue } from '../../types/common'
6
7
 
7
8
  export interface TreeViewItemProps {
9
+ /** Additional CSS classes */
10
+ class?: ClassValue
8
11
  /** Node data */
9
12
  node: TreeNode
10
13
  }
@@ -86,6 +89,7 @@ provide(TreeViewItemKey, {
86
89
  })
87
90
 
88
91
  const itemClasses = computed(() => [
92
+ props.class,
89
93
  'ui-treeview__item',
90
94
  {
91
95
  'ui-treeview__item--selected': isSelected.value,
package/src/index.ts CHANGED
@@ -359,3 +359,6 @@ export type { ParsedTime, TimeFormat } from './utils/time'
359
359
 
360
360
  // Configuration
361
361
  export { configureIcons, resetIcons, type IconMap } from './config/icons'
362
+
363
+ // Types
364
+ export type { ClassValue } from './types/common'
@@ -1,25 +1,25 @@
1
- /* Main CSS Entry Point - Import order matters */
1
+ /* Layer order declaration - MUST come first */
2
+ @layer spire-base, spire-theme, spire-components;
2
3
 
3
- @import './fallback.css';
4
- @import './tokens.css';
5
- @import './theme.css';
6
- @import './mood.css';
7
- @import './depth.css';
8
- @import './motion.css';
9
- @import './effects.css';
10
- @import './reset.css';
4
+ /* Base layer: resets, fallbacks, tokens */
5
+ @import './fallback.css' layer(spire-base);
6
+ @import './tokens.css' layer(spire-base);
7
+ @import './reset.css' layer(spire-base);
11
8
 
12
- /* Utility: Avatar Group (overlapping avatars) */
13
- .ui-avatar-group {
14
- display: flex;
15
- flex-direction: row-reverse;
16
- justify-content: flex-end;
17
- }
18
-
19
- .ui-avatar-group > * {
20
- margin-left: -0.5rem;
21
- }
9
+ /* Theme layer: semantic tokens, moods, depth, motion, effects */
10
+ @import './theme.css' layer(spire-theme);
11
+ @import './mood.css' layer(spire-theme);
12
+ @import './depth.css' layer(spire-theme);
13
+ @import './motion.css' layer(spire-theme);
14
+ @import './effects.css' layer(spire-theme);
22
15
 
23
- .ui-avatar-group > *:last-child {
24
- margin-left: 0;
16
+ /* Components layer: global component utilities */
17
+ @layer spire-components {
18
+ .ui-avatar-group {
19
+ display: flex;
20
+ flex-direction: row-reverse;
21
+ justify-content: flex-end;
22
+ }
23
+ .ui-avatar-group > * { margin-left: -0.5rem; }
24
+ .ui-avatar-group > *:last-child { margin-left: 0; }
25
25
  }
@@ -0,0 +1,4 @@
1
+ import type { HTMLAttributes } from 'vue'
2
+
3
+ /** CSS class value - string, array, or object */
4
+ export type ClassValue = HTMLAttributes['class']