@globalbrain/sefirot 4.8.0 → 4.10.0

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.
@@ -1,13 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  import IconCheckCircleFill from '~icons/ph/check-circle-fill'
3
- import IconCircle from '~icons/ph/circle-bold'
4
- import IconCircleDashed from '~icons/ph/circle-dashed-bold'
5
- import IconCircleNotch from '~icons/ph/circle-notch-bold'
6
- import IconXCircle from '~icons/ph/x-circle-bold'
3
+ import IconCircle from '~icons/ph/circle'
4
+ import IconCircleDashed from '~icons/ph/circle-dashed'
5
+ import IconCircleNotch from '~icons/ph/circle-notch'
6
+ import IconMinusCircle from '~icons/ph/minus-circle'
7
+ import IconXCircle from '~icons/ph/x-circle'
7
8
  import { computed } from 'vue'
8
9
 
9
- export type Size = 'nano' | 'mini' | 'small' | 'medium' | 'large' | 'jumbo'
10
- export type State = 'pending' | 'ready' | 'queued' | 'running' | 'completed' | 'failed'
10
+ export type Size = 'nano' | 'mini' | 'small' | 'medium' | 'large' | 'jumbo' | 'fill'
11
+ export type State = 'pending' | 'ready' | 'queued' | 'running' | 'completed' | 'failed' | 'aborted'
11
12
  export type Mode = 'colored' | 'monochrome'
12
13
 
13
14
  const props = withDefaults(defineProps<{
@@ -33,6 +34,7 @@ const classes = computed(() => [
33
34
  <IconCircleNotch v-if="props.state === 'running'" class="icon" />
34
35
  <IconCheckCircleFill v-if="props.state === 'completed'" class="icon" />
35
36
  <IconXCircle v-if="props.state === 'failed'" class="icon" />
37
+ <IconMinusCircle v-if="props.state === 'aborted'" class="icon aborted" />
36
38
  </div>
37
39
  </template>
38
40
 
@@ -51,6 +53,10 @@ const classes = computed(() => [
51
53
  .icon {
52
54
  width: 100%;
53
55
  height: 100%;
56
+
57
+ &.aborted {
58
+ transform: rotate(-45deg);
59
+ }
54
60
  }
55
61
 
56
62
  .SIndicator.nano { width: 20px; height: 20px; }
@@ -59,6 +65,7 @@ const classes = computed(() => [
59
65
  .SIndicator.medium { width: 32px; height: 32px; }
60
66
  .SIndicator.large { width: 40px; height: 40px; }
61
67
  .SIndicator.jumbo { width: 48px; height: 48px; }
68
+ .SIndicator.fill { width: 100%; height: 100%; }
62
69
 
63
70
  .SIndicator.queued {
64
71
  animation: indicator-blink 1.5s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
@@ -75,6 +82,7 @@ const classes = computed(() => [
75
82
  &.running { color: var(--c-fg-info-1); }
76
83
  &.completed { color: var(--c-fg-success-1); }
77
84
  &.failed { color: var(--c-fg-danger-1); }
85
+ &.aborted { color: var(--c-fg-gray-1); }
78
86
  }
79
87
 
80
88
  .SIndicator.monochrome {
@@ -1,18 +1,42 @@
1
- <script setup lang="ts">
1
+ <script setup lang="ts" generic="T extends ModelType = 'file'">
2
+ import { type ValidationRuleWithParams } from '@vuelidate/core'
3
+ import { useDropZone } from '@vueuse/core'
2
4
  import { type Component, computed, ref } from 'vue'
3
5
  import { useTrans } from '../composables/Lang'
4
6
  import { type Validatable } from '../composables/Validation'
5
7
  import { formatSize } from '../support/File'
6
- import SButton from './SButton.vue'
8
+ import SButton, { type Mode as ButtonMode } from './SButton.vue'
7
9
  import SCard from './SCard.vue'
8
10
  import SCardBlock from './SCardBlock.vue'
11
+ import { type State as IndicatorState } from './SIndicator.vue'
9
12
  import SInputBase from './SInputBase.vue'
10
13
  import SInputFileUploadItem from './SInputFileUploadItem.vue'
14
+ import STrans from './STrans.vue'
11
15
 
12
16
  export type Size = 'mini' | 'small' | 'medium'
13
17
  export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
14
18
 
15
- const props = defineProps<{
19
+ export type ModelType = 'file' | 'object'
20
+ export type ModelValue<T extends ModelType> = T extends 'file' ? File : FileObject
21
+
22
+ export interface FileObject {
23
+ file: File
24
+ indicatorState?: IndicatorState | null
25
+ canRemove?: boolean
26
+ action?: Action
27
+ errorMessage?: string | null
28
+ }
29
+
30
+ export interface Action {
31
+ mode?: ButtonMode
32
+ icon?: Component
33
+ leadIcon?: Component
34
+ trailIcon?: Component
35
+ label?: string
36
+ onClick(): void
37
+ }
38
+
39
+ const props = withDefaults(defineProps<{
16
40
  size?: Size
17
41
  label?: string
18
42
  info?: string
@@ -26,15 +50,20 @@ const props = defineProps<{
26
50
  checkIcon?: Component
27
51
  checkText?: string
28
52
  checkColor?: Color
29
- value?: File[]
30
- modelValue?: File[]
31
- hideError?: boolean
53
+ droppable?: boolean
54
+ value?: ModelValue<T>[]
55
+ modelType?: T
56
+ modelValue?: ModelValue<T>[]
57
+ rules?: Record<string, ValidationRuleWithParams>
32
58
  validation?: Validatable
33
- }>()
59
+ hideError?: boolean
60
+ }>(), {
61
+ modelType: 'file' as any // `ModelType` doesn't work so stubbing it.
62
+ })
34
63
 
35
64
  const emit = defineEmits<{
36
- 'update:model-value': [files: File[]]
37
- 'change': [files: File[]]
65
+ 'update:model-value': [files: ModelValue<T>[]]
66
+ 'change': [files: ModelValue<T>[]]
38
67
  }>()
39
68
 
40
69
  const { t } = useTrans({
@@ -50,28 +79,46 @@ const { t } = useTrans({
50
79
  }
51
80
  })
52
81
 
82
+ const dropZoneEl = ref<HTMLDivElement | null>(null)
83
+
84
+ const { isOverDropZone } = useDropZone(dropZoneEl, {
85
+ multiple: true,
86
+ onDrop: (files) => onDrop(files)
87
+ })
88
+
53
89
  const _value = computed(() => {
54
90
  return props.modelValue !== undefined
55
91
  ? props.modelValue
56
- : props.value !== undefined ? props.value : []
92
+ : props.value !== undefined ? props.value : [] as ModelValue<T>[]
57
93
  })
58
94
 
59
95
  const input = ref<HTMLInputElement | null>(null)
60
96
 
61
- const classes = computed(() => [props.size ?? 'small'])
97
+ const classes = computed(() => [
98
+ props.size ?? 'small',
99
+ { droppable: props.droppable },
100
+ { 'is-over-drop-zone': isOverDropZone.value }
101
+ ])
62
102
 
63
103
  const totalFileCountText = computed(() => {
64
104
  return t.selected_files(_value.value.length)
65
105
  })
66
106
 
67
107
  const totalFileSizeText = computed(() => {
68
- return formatSize(_value.value)
108
+ const files = _value.value.map((file) => file instanceof File ? file : file.file)
109
+ return formatSize(files)
69
110
  })
70
111
 
71
112
  function open() {
72
113
  input.value!.click()
73
114
  }
74
115
 
116
+ function onDrop(files: File[] | null) {
117
+ if (files !== null && files.length > 0) {
118
+ emitChange(append(files))
119
+ }
120
+ }
121
+
75
122
  function onChange(e: Event) {
76
123
  const files = Array.from((e.target as HTMLInputElement).files ?? [])
77
124
 
@@ -81,25 +128,35 @@ function onChange(e: Event) {
81
128
  return
82
129
  }
83
130
 
84
- const newFiles = [..._value.value, ...files]
85
-
86
- emit('update:model-value', newFiles)
87
- emit('change', newFiles)
88
-
89
- props.validation?.$touch()
131
+ emitChange(append(files))
90
132
  }
91
133
 
92
134
  function onRemove(index: number) {
93
135
  const files = _value.value.filter((_, i) => i !== index)
136
+ emitChange(files)
137
+ }
94
138
 
139
+ function emitChange(files: ModelValue<T>[]) {
95
140
  emit('update:model-value', files)
96
141
  emit('change', files)
142
+ props.validation?.$touch()
143
+ }
144
+
145
+ function append(files: File[]) {
146
+ return [
147
+ ..._value.value,
148
+ ...(props.modelType === 'file' ? files : toFileObjects(files))
149
+ ] as ModelValue<T>[]
150
+ }
151
+
152
+ function toFileObjects(files: File[]) {
153
+ return files.map((file) => ({ file } as ModelValue<T>))
97
154
  }
98
155
  </script>
99
156
 
100
157
  <template>
101
158
  <SInputBase
102
- class="SInputFile"
159
+ class="SInputFileUpload"
103
160
  :class="classes"
104
161
  :label="label"
105
162
  :note="note"
@@ -121,8 +178,29 @@ function onRemove(index: number) {
121
178
  @change="onChange"
122
179
  >
123
180
  <SCard :mode="hasError ? 'danger' : undefined">
124
- <SCardBlock class="header">
181
+ <SCardBlock v-if="droppable" class="drop-zone" ref="dropZoneEl" @click="open">
182
+ <div class="drop-zone-box">
183
+ <STrans lang="en">
184
+ <div class="drop-zone-text">
185
+ Drag and drop files here, or
186
+ </div>
187
+ <div class="drop-zone-action">
188
+ <SButton size="mini" label="Select files" />
189
+ </div>
190
+ </STrans>
191
+ <STrans lang="ja">
192
+ <div class="drop-zone-text">
193
+ ファイルをドラック&ドロップ、または
194
+ </div>
195
+ <div class="drop-zone-action">
196
+ <SButton size="mini" label="ファイルを選択" />
197
+ </div>
198
+ </STrans>
199
+ </div>
200
+ </SCardBlock>
201
+ <SCardBlock v-if="!droppable || placeholder" class="header">
125
202
  <SButton
203
+ v-if="!droppable"
126
204
  size="small"
127
205
  :label="text ?? t.button_text"
128
206
  @click="open"
@@ -134,8 +212,9 @@ function onRemove(index: number) {
134
212
  <template v-if="_value.length">
135
213
  <SInputFileUploadItem
136
214
  v-for="file, i in _value"
137
- :key="file.name"
215
+ :key="i"
138
216
  :file="file"
217
+ :rules="rules"
139
218
  @remove="() => { onRemove(i) }"
140
219
  />
141
220
  </template>
@@ -172,6 +251,35 @@ function onRemove(index: number) {
172
251
  display: none;
173
252
  }
174
253
 
254
+ .drop-zone {
255
+ padding: 12px;
256
+
257
+ &:hover .drop-zone-box {
258
+ border-color: var(--c-border-info-1);
259
+ cursor: pointer;
260
+ }
261
+ }
262
+
263
+ .drop-zone-box {
264
+ display: flex;
265
+ flex-direction: column;
266
+ justify-content: center;
267
+ align-items: center;
268
+ gap: 16px;
269
+ border: 1px dashed var(--c-border-mute-1);
270
+ border-radius: 3px;
271
+ padding: 24px 0;
272
+ min-height: 192px;
273
+ text-align: center;
274
+ transition: border-color 0.25s;
275
+ }
276
+
277
+ .drop-zone-text {
278
+ text-align: center;
279
+ font-size: 14px;
280
+ color: var(--c-text-2);
281
+ }
282
+
175
283
  .header {
176
284
  display: flex;
177
285
  align-items: center;
@@ -226,4 +334,16 @@ function onRemove(index: number) {
226
334
  width: 32px;
227
335
  height: 32px;
228
336
  }
337
+
338
+ .SInputFileUpload.droppable {
339
+ .header {
340
+ padding-left: 16px;
341
+ }
342
+ }
343
+
344
+ .SInputFileUpload.is-over-drop-zone {
345
+ .drop-zone-box {
346
+ border-color: var(--c-border-info-1);
347
+ }
348
+ }
229
349
  </style>
@@ -1,38 +1,99 @@
1
1
  <script setup lang="ts">
2
2
  import IconFileText from '~icons/ph/file-text'
3
3
  import IconTrash from '~icons/ph/trash'
4
- import { computed } from 'vue'
4
+ import { type ValidationRuleWithParams } from '@vuelidate/core'
5
+ import { type Component, computed } from 'vue'
6
+ import { useValidation } from '../composables/Validation'
5
7
  import { formatSize } from '../support/File'
6
- import SButton from './SButton.vue'
8
+ import SButton, { type Mode as ButtonMode } from './SButton.vue'
7
9
  import SCardBlock from './SCardBlock.vue'
10
+ import SIndicator, { type State as IndicatorState } from './SIndicator.vue'
8
11
 
9
- const props = defineProps<{
12
+ export interface FileObject {
10
13
  file: File
14
+ indicatorState?: IndicatorState | null
15
+ canRemove?: boolean
16
+ action?: Action | null
17
+ errorMessage?: string | null
18
+ }
19
+
20
+ export interface Action {
21
+ mode?: ButtonMode
22
+ icon?: Component
23
+ leadIcon?: Component
24
+ trailIcon?: Component
25
+ label?: string
26
+ onClick(): void
27
+ }
28
+
29
+ const props = defineProps<{
30
+ file: File | FileObject
31
+ rules?: Record<string, ValidationRuleWithParams>
11
32
  }>()
12
33
 
13
34
  defineEmits<{
14
35
  'remove': []
15
36
  }>()
16
37
 
17
- const fileSize = computed(() => {
18
- return formatSize(props.file)
38
+ const _file = computed(() => ({
39
+ name: props.file instanceof File ? props.file.name : props.file.file.name,
40
+ file: props.file instanceof File ? props.file : props.file.file,
41
+ size: formatSize(props.file instanceof File ? props.file : props.file.file),
42
+ indicatorState: props.file instanceof File ? null : props.file.indicatorState,
43
+ canRemove: props.file instanceof File ? true : props.file.canRemove ?? true,
44
+ action: props.file instanceof File ? null : props.file.action,
45
+ errorMessage: props.file instanceof File ? null : props.file.errorMessage
46
+ }))
47
+
48
+ const { validation } = useValidation(() => ({
49
+ file: _file.value.file
50
+ }), {
51
+ file: props.rules ?? {}
19
52
  })
53
+
54
+ validation.value.$touch()
20
55
  </script>
21
56
 
22
57
  <template>
23
58
  <SCardBlock class="SInputFileUploadItem">
24
- <p class="name">
25
- <IconFileText class="name-icon" />
26
- <span class="name-text">{{ file.name }}</span>
27
- </p>
28
- <div class="size">{{ fileSize }}</div>
29
- <SButton
30
- size="small"
31
- type="text"
32
- mode="mute"
33
- :icon="IconTrash"
34
- @click="$emit('remove')"
35
- />
59
+ <div class="name">
60
+ <div class="name-label">
61
+ <div class="name-icon">
62
+ <IconFileText v-if="_file.indicatorState == null" class="name-icon-svg" />
63
+ <SIndicator size="fill" v-else :state="_file.indicatorState" />
64
+ </div>
65
+ <p class="name-text">{{ _file.name }}</p>
66
+ </div>
67
+ <p v-if="_file.errorMessage" class="error">{{ _file.errorMessage }}</p>
68
+ <p v-else-if="validation.$errors.length" class="error">{{ validation.$errors[0]?.$message }}</p>
69
+ </div>
70
+ <div v-if="_file.action" class="action">
71
+ <SButton
72
+ type="text"
73
+ size="small"
74
+ :mode="_file.action.mode"
75
+ :icon="_file.action.icon"
76
+ :lead-icon="_file.action.leadIcon"
77
+ :trail-icon="_file.action.trailIcon"
78
+ :label="_file.action.label"
79
+ @click="_file.action.onClick"
80
+ />
81
+ </div>
82
+ <div class="meta">
83
+ <div class="size">
84
+ {{ _file.size }}
85
+ </div>
86
+ <div class="delete">
87
+ <SButton
88
+ size="small"
89
+ type="text"
90
+ mode="mute"
91
+ :icon="IconTrash"
92
+ :disabled="!_file.canRemove"
93
+ @click="$emit('remove')"
94
+ />
95
+ </div>
96
+ </div>
36
97
  </SCardBlock>
37
98
  </template>
38
99
 
@@ -40,12 +101,20 @@ const fileSize = computed(() => {
40
101
  .SInputFileUploadItem {
41
102
  display: flex;
42
103
  align-items: center;
43
- gap: 8px;
44
- height: 48px;
45
- padding: 0 8px 0 16px;
104
+ gap: 16px;
105
+ padding: 8px 8px 8px 16px;
46
106
  }
47
107
 
48
108
  .name {
109
+ display: flex;
110
+ flex-direction: column;
111
+ flex-grow: 1;
112
+ white-space: nowrap;
113
+ overflow: hidden;
114
+ text-overflow: ellipsis;
115
+ }
116
+
117
+ .name-label {
49
118
  display: flex;
50
119
  align-items: center;
51
120
  gap: 8px;
@@ -56,6 +125,12 @@ const fileSize = computed(() => {
56
125
  }
57
126
 
58
127
  .name-icon {
128
+ flex-shrink: 0;
129
+ width: 16px;
130
+ height: 16px;
131
+ }
132
+
133
+ .name-icon-svg {
59
134
  width: 16px;
60
135
  height: 16px;
61
136
  color: var(--c-text-2);
@@ -70,9 +145,35 @@ const fileSize = computed(() => {
70
145
  text-overflow: ellipsis;
71
146
  }
72
147
 
148
+ .error {
149
+ padding-left: 24px;
150
+ line-height: 20px;
151
+ font-size: 12px;
152
+ color: var(--c-text-danger-1);
153
+ white-space: nowrap;
154
+ overflow: hidden;
155
+ text-overflow: ellipsis;
156
+ }
157
+
158
+ .action {
159
+ flex-shrink: 0;
160
+ }
161
+
162
+ .meta {
163
+ display: flex;
164
+ align-items: center;
165
+ flex-shrink: 0;
166
+ gap: 8px;
167
+ }
168
+
73
169
  .size {
170
+ flex-shrink: 0;
74
171
  line-height: 24px;
75
172
  font-size: 12px;
76
173
  color: var(--c-text-2);
77
174
  }
175
+
176
+ .delete {
177
+ flex-shrink: 0;
178
+ }
78
179
  </style>
@@ -7,7 +7,9 @@ import STableCellAvatars from './STableCellAvatars.vue'
7
7
  import STableCellCustom from './STableCellCustom.vue'
8
8
  import STableCellDay from './STableCellDay.vue'
9
9
  import STableCellEmpty from './STableCellEmpty.vue'
10
+ import STableCellIndicator from './STableCellIndicator.vue'
10
11
  import STableCellNumber from './STableCellNumber.vue'
12
+ import STableCellPath from './STableCellPath.vue'
11
13
  import STableCellPill from './STableCellPill.vue'
12
14
  import STableCellPills from './STableCellPills.vue'
13
15
  import STableCellState from './STableCellState.vue'
@@ -56,6 +58,10 @@ const computedCell = computed<TableCell | undefined>(() =>
56
58
  :icon-color="computedCell.iconColor"
57
59
  :on-click="computedCell.onClick"
58
60
  />
61
+ <STableCellPath
62
+ v-else-if="computedCell.type === 'path'"
63
+ :segments="computedCell.segments"
64
+ />
59
65
  <STableCellDay
60
66
  v-else-if="computedCell.type === 'day'"
61
67
  :align="computedCell.align"
@@ -77,6 +83,11 @@ const computedCell = computed<TableCell | undefined>(() =>
77
83
  :state="computedCell.label"
78
84
  :mode="computedCell.mode"
79
85
  />
86
+ <STableCellIndicator
87
+ v-else-if="computedCell.type === 'indicator'"
88
+ :state="computedCell.state"
89
+ :label="computedCell.label"
90
+ />
80
91
  <STableCellAvatar
81
92
  v-else-if="computedCell.type === 'avatar'"
82
93
  :value="value"
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import SIndicator, { type State } from './SIndicator.vue'
3
+
4
+ defineProps<{
5
+ state: State
6
+ label?: string | null
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div class="STableCellIndicator" :class="[state]">
12
+ <SIndicator size="nano" :state="state" />
13
+ <div v-if="label" class="text">{{ label }}</div>
14
+ </div>
15
+ </template>
16
+
17
+ <style scoped lang="postcss">
18
+ .STableCellIndicator {
19
+ display: flex;
20
+ align-items: center;
21
+ gap: 8px;
22
+ padding: 0 16px;
23
+ min-height: 40px;
24
+ }
25
+
26
+ .text {
27
+ line-height: 24px;
28
+ font-size: 14px;
29
+ }
30
+
31
+ .STableCellIndicator.pending .text { color: var(--c-text-1); }
32
+ .STableCellIndicator.ready .text { color: var(--c-text-1); }
33
+ .STableCellIndicator.queued .text { color: var(--c-text-1); }
34
+ .STableCellIndicator.running .text { color: var(--c-text-1); }
35
+ .STableCellIndicator.completed .text { color: var(--c-text-1); }
36
+ .STableCellIndicator.failed .text { color: var(--c-text-3); }
37
+ .STableCellIndicator.aborted .text { color: var(--c-text-3); }
38
+ </style>
@@ -0,0 +1,71 @@
1
+ <script setup lang="ts">
2
+ import { type TableCellPathSegment } from '../composables/Table'
3
+ import SLink from './SLink.vue'
4
+
5
+ defineProps<{
6
+ segments: TableCellPathSegment[]
7
+ }>()
8
+
9
+ function classes(item: TableCellPathSegment) {
10
+ return [
11
+ item.color ?? 'neutral',
12
+ { link: !!item.link || !!item.onClick }
13
+ ]
14
+ }
15
+ </script>
16
+
17
+ <template>
18
+ <div class="STableCellPath">
19
+ <template v-for="segment, index in segments" :key="index">
20
+ <div v-if="index > 0" class="divider">/</div>
21
+ <SLink
22
+ class="text"
23
+ :class="classes(segment)"
24
+ :href="segment.link"
25
+ @click="segment.onClick"
26
+ >
27
+ {{ segment.text }}
28
+ </SLink>
29
+ </template>
30
+ </div>
31
+ </template>
32
+
33
+ <style scoped lang="postcss">
34
+ .STableCellPath {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 6px;
38
+ padding: 0 16px;
39
+ min-height: 40px;
40
+ }
41
+
42
+ .divider {
43
+ color: var(--c-text-3);
44
+ }
45
+
46
+ .text {
47
+ line-height: 24px;
48
+ font-size: 14px;
49
+ transition: color 0.25s;
50
+
51
+ &.neutral { color: var(--c-text-1); }
52
+ &.soft { color: var(--c-text-2); }
53
+ &.mute { color: var(--c-text-3); }
54
+ &.info { color: var(--c-text-info-1); }
55
+ &.success { color: var(--c-text-success-1); }
56
+ &.warning { color: var(--c-text-warning-1); }
57
+ &.danger { color: var(--c-text-danger-1); }
58
+
59
+ &.link {
60
+ cursor: pointer;
61
+ }
62
+
63
+ &.link.neutral:hover { color: var(--c-text-info-1); }
64
+ &.link.soft:hover { color: var(--c-text-info-1); }
65
+ &.link.mute:hover { color: var(--c-text-info-1); }
66
+ &.link.info:hover { color: var(--c-text-info-2); }
67
+ &.link.success:hover { color: var(--c-text-success-2); }
68
+ &.link.warning:hover { color: var(--c-text-warning-2); }
69
+ &.link.danger:hover { color: var(--c-text-danger-2); }
70
+ }
71
+ </style>
@@ -1,5 +1,6 @@
1
1
  import { type Component, type MaybeRef, type MaybeRefOrGetter } from 'vue'
2
- import { type Mode } from '../components/SButton.vue'
2
+ import { type Mode as ButtonMode } from '../components/SButton.vue'
3
+ import { type State as IndicatorState } from '../components/SIndicator.vue'
3
4
  import { type Day } from '../support/Day'
4
5
  import { type DropdownSection } from './Dropdown'
5
6
  import { type Position } from './Tooltip'
@@ -55,10 +56,12 @@ export type TableColumnCellFn<V, R> = (value: V, record: R) => TableCell<V, R>
55
56
  export type TableCell<V = any, R = any> =
56
57
  | TableCellText<V, R>
57
58
  | TableCellNumber<V, R>
59
+ | TableCellPath
58
60
  | TableCellDay
59
61
  | TableCellPill
60
62
  | TableCellPills
61
63
  | TableCellState
64
+ | TableCellIndicator
62
65
  | TableCellAvatar<V, R>
63
66
  | TableCellAvatars
64
67
  | TableCellCustom
@@ -69,10 +72,12 @@ export type TableCell<V = any, R = any> =
69
72
  export type TableCellType =
70
73
  | 'text'
71
74
  | 'number'
75
+ | 'path'
72
76
  | 'day'
73
77
  | 'pill'
74
78
  | 'pills'
75
79
  | 'state'
80
+ | 'indicator'
76
81
  | 'avatar'
77
82
  | 'avatars'
78
83
  | 'custom'
@@ -116,6 +121,18 @@ export interface TableCellNumber<V = any, R = any> extends TableCellBase {
116
121
  onClick?(value: V, record: R): void
117
122
  }
118
123
 
124
+ export interface TableCellPath extends TableCellBase {
125
+ type: 'path'
126
+ segments: TableCellPathSegment[]
127
+ }
128
+
129
+ export interface TableCellPathSegment {
130
+ text: string
131
+ link?: string | null
132
+ color?: TableCellValueColor
133
+ onClick?(): void
134
+ }
135
+
119
136
  export type TableCellValueColor = ColorModes | 'soft'
120
137
 
121
138
  export interface TableCellDay extends TableCellBase {
@@ -187,17 +204,23 @@ export interface TableCellState extends TableCellBase {
187
204
  mode?: ColorModes
188
205
  }
189
206
 
207
+ export interface TableCellIndicator extends TableCellBase {
208
+ type: 'indicator'
209
+ state: IndicatorState
210
+ label?: string | null
211
+ }
212
+
190
213
  export interface TableCellActions<R = any> extends TableCellBase {
191
214
  type: 'actions'
192
215
  actions: TableCellAction<R>[]
193
216
  }
194
217
 
195
218
  export interface TableCellAction<R = any> {
196
- mode?: Mode
219
+ mode?: ButtonMode
197
220
  icon?: Component
198
- iconMode?: Mode
221
+ iconMode?: ButtonMode
199
222
  label?: string
200
- labelMode?: Mode
223
+ labelMode?: ButtonMode
201
224
  onClick(record: R): void
202
225
  show?(record: R): boolean
203
226
  }
@@ -210,9 +233,9 @@ export interface TableMenu {
210
233
 
211
234
  export interface TableHeaderAction {
212
235
  show?: boolean
213
- mode?: Mode
236
+ mode?: ButtonMode
214
237
  label: string
215
- labelMode?: Mode
238
+ labelMode?: ButtonMode
216
239
  onClick(): void
217
240
  }
218
241
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
3
  "type": "module",
4
- "version": "4.8.0",
4
+ "version": "4.10.0",
5
5
  "packageManager": "pnpm@9.15.3",
6
6
  "description": "Vue Components for Global Brain Design System.",
7
7
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",