@globalbrain/sefirot 2.3.0 → 2.5.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.
@@ -125,7 +125,6 @@ function handleClick(option: DropdownSectionFilterOption, value: string | number
125
125
 
126
126
  .button {
127
127
  display: flex;
128
- align-items: center;
129
128
  border-radius: 6px;
130
129
  padding: 0 8px;
131
130
  width: 100%;
@@ -145,6 +144,7 @@ function handleClick(option: DropdownSectionFilterOption, value: string | number
145
144
 
146
145
  .checkbox {
147
146
  display: block;
147
+ padding-top: 9px;
148
148
  }
149
149
 
150
150
  .checkbox-box {
@@ -177,6 +177,11 @@ function handleClick(option: DropdownSectionFilterOption, value: string | number
177
177
  }
178
178
  }
179
179
 
180
+ .option-item {
181
+ padding-left: 8px;
182
+ width: 100%;
183
+ }
184
+
180
185
  .empty {
181
186
  padding: 16px;
182
187
  font-size: 12px;
@@ -9,23 +9,32 @@ defineProps<{
9
9
 
10
10
  <template>
11
11
  <span class="SDropdownSectionFilterItemAvatar">
12
- <SAvatar size="nano" :avatar="image" :name="label" />
12
+ <div class="avatar">
13
+ <SAvatar size="nano" :avatar="image" :name="label" />
14
+ </div>
13
15
  <span class="name">{{ label }}</span>
14
16
  </span>
15
17
  </template>
16
18
 
17
19
  <style scoped lang="postcss">
18
20
  .SDropdownSectionFilterItemAvatar {
19
- display: inline-flex;
21
+ display: flex;
20
22
  align-items: center;
21
- padding-left: 8px;
23
+ padding-right: 16px;
22
24
  min-height: 32px;
23
25
  }
24
26
 
27
+ .avatar {
28
+ flex-shrink: 0;
29
+ }
30
+
25
31
  .name {
26
32
  display: inline-block;
27
33
  padding-left: 8px;
28
34
  font-size: 12px;
29
35
  font-weight: 500;
36
+ white-space: nowrap;
37
+ overflow: hidden;
38
+ text-overflow: ellipsis;
30
39
  }
31
40
  </style>
@@ -6,14 +6,18 @@ defineProps<{
6
6
 
7
7
  <template>
8
8
  <span class="SDropdownSectionFilterItemText">
9
- {{ label }}
9
+ <span class="text">{{ label }}</span>
10
10
  </span>
11
11
  </template>
12
12
 
13
13
  <style scoped lang="postcss">
14
14
  .SDropdownSectionFilterItemText {
15
- padding-left: 8px;
16
- line-height: 32px;
15
+ display: inline-block;
16
+ padding: 6px 0;
17
+ }
18
+
19
+ .text {
20
+ line-height: 20px;
17
21
  font-size: 12px;
18
22
  font-weight: 500;
19
23
  }
@@ -57,6 +57,13 @@ defineProps<{
57
57
  :avatars="cell.avatars"
58
58
  :color="cell.color"
59
59
  />
60
+ <component
61
+ v-else-if="cell.type === 'component'"
62
+ :is="cell.component"
63
+ :value="value"
64
+ :record="record"
65
+ v-bind="cell.props"
66
+ />
60
67
  </div>
61
68
  </template>
62
69
 
@@ -0,0 +1,108 @@
1
+ import { watchOnce } from '@vueuse/core'
2
+ import cloneDeep from 'lodash-es/cloneDeep'
3
+ import { WatchSource, reactive } from 'vue'
4
+ import { isObject } from '../support/Utils'
5
+
6
+ export interface Data<T extends Record<string, any>> {
7
+ state: T
8
+ init(): void
9
+ }
10
+
11
+ export type DataWithDef = Record<string, any>
12
+
13
+ export interface Def<T = any> {
14
+ __isDef: true
15
+ value: any
16
+ source: WatchSource<T>
17
+ cb: (value: Exclude<T, undefined>) => void
18
+ }
19
+
20
+ export type UseDataInput<
21
+ T extends Record<string, any>
22
+ > = T | ((utils: UseDataInputUtils) => DataWithDef)
23
+
24
+ export interface UseDataInputUtils {
25
+ def<T>(
26
+ value: any,
27
+ source: WatchSource<T>,
28
+ cb: (value: Exclude<T, undefined>) => void
29
+ ): Def<T>
30
+ }
31
+
32
+ export function useData<T extends Record<string, any>>(
33
+ data: UseDataInput<T>
34
+ ): Data<T> {
35
+ const { state, defs } = createState(data)
36
+
37
+ const initialState = cloneDeep(state)
38
+ const reactiveState = reactive(state)
39
+
40
+ handleDefs(defs, reactiveState)
41
+
42
+ function init(): void {
43
+ Object.assign(reactiveState, initialState)
44
+ }
45
+
46
+ return {
47
+ state: reactiveState,
48
+ init
49
+ }
50
+ }
51
+
52
+ function createState<T extends Record<string, any>>(
53
+ data: UseDataInput<T>
54
+ ): { state: T; defs: [string, Def][] } {
55
+ if (typeof data !== 'function') {
56
+ return { state: data, defs: [] }
57
+ }
58
+
59
+ const dataWithDef = data({ def })
60
+
61
+ const state = {} as T
62
+ const defs = [] as [string, Def][]
63
+
64
+ for (const key in dataWithDef) {
65
+ const maybeDef = dataWithDef[key]
66
+
67
+ if (!isDef(maybeDef)) {
68
+ (state as any)[key] = maybeDef
69
+ continue
70
+ }
71
+
72
+ (state as any)[key] = maybeDef.value
73
+
74
+ defs.push([key, maybeDef])
75
+ }
76
+
77
+ return {
78
+ state,
79
+ defs
80
+ }
81
+ }
82
+
83
+ function handleDefs<T extends Record<string, any>>(
84
+ defs: [string, Def][], state: T
85
+ ): void {
86
+ defs.forEach(([key, def]) => {
87
+ watchOnce(def.source, (value: any) => {
88
+ (state as any)[key] = def.cb(value)
89
+ })
90
+ })
91
+ }
92
+
93
+ function def<T>(
94
+ value: any,
95
+ source: WatchSource<T>,
96
+ cb: (value: Exclude<T, undefined>) => void
97
+ ): Def {
98
+ return {
99
+ __isDef: true,
100
+ value,
101
+ source,
102
+ cb
103
+ }
104
+ }
105
+
106
+ function isDef(value: any): boolean {
107
+ return isObject(value) ? !!value.__isDef : false
108
+ }
@@ -1,6 +1,6 @@
1
- import cloneDeep from 'lodash-es/cloneDeep'
2
- import { Ref, computed, reactive } from 'vue'
1
+ import { Ref, computed } from 'vue'
3
2
  import { useSnackbars } from '../stores/Snackbars'
3
+ import { UseDataInput, useData } from './Data'
4
4
  import { Validation, useValidation } from './Validation'
5
5
 
6
6
  export interface Form<T extends Record<string, any>> {
@@ -13,7 +13,7 @@ export interface Form<T extends Record<string, any>> {
13
13
  }
14
14
 
15
15
  export interface UseFormOptions<T extends Record<string, any>> {
16
- data: T
16
+ data: UseDataInput<T>
17
17
  rules?: Record<string, any> | ((state: T) => Record<string, any>)
18
18
  }
19
19
 
@@ -22,20 +22,18 @@ export function useForm<
22
22
  >(options: UseFormOptions<T>): Form<T> {
23
23
  const snackbars = useSnackbars()
24
24
 
25
- const initialData = cloneDeep(options.data)
26
-
27
- const data = reactive(options.data)
25
+ const data = useData(options.data)
28
26
 
29
27
  const rules = computed(() => {
30
28
  return options.rules
31
- ? typeof options.rules === 'function' ? options.rules(data) : options.rules
29
+ ? typeof options.rules === 'function' ? options.rules(data.state) : options.rules
32
30
  : {}
33
31
  })
34
32
 
35
- const validation = useValidation(data, rules)
33
+ const validation = useValidation(data.state, rules)
36
34
 
37
35
  function init(): void {
38
- Object.assign(data, initialData)
36
+ data.init()
39
37
  reset()
40
38
  }
41
39
 
@@ -61,7 +59,7 @@ export function useForm<
61
59
  }
62
60
 
63
61
  return {
64
- data,
62
+ data: data.state,
65
63
  init,
66
64
  reset,
67
65
  validation,
@@ -1,5 +1,5 @@
1
1
  import { MaybeRef } from '@vueuse/core'
2
- import { reactive } from 'vue'
2
+ import { Component, reactive } from 'vue'
3
3
  import { DropdownSection } from './Dropdown'
4
4
 
5
5
  export interface Table {
@@ -34,8 +34,9 @@ export type TableCell =
34
34
  | TableCellPill
35
35
  | TableCellAvatar
36
36
  | TableCellAvatars
37
+ | TableCellComponent
37
38
 
38
- export type TableCellType = 'text' | 'day' | 'pill' | 'avatar' | 'avatars'
39
+ export type TableCellType = 'text' | 'day' | 'pill' | 'avatar' | 'avatars' | 'component'
39
40
 
40
41
  export interface TableCellBase {
41
42
  type: TableCellType
@@ -78,6 +79,12 @@ export interface TableCellAvatars extends TableCellBase {
78
79
  color?: 'neutral' | 'soft' | 'mute'
79
80
  }
80
81
 
82
+ export interface TableCellComponent extends TableCellBase {
83
+ type: 'component'
84
+ component: Component
85
+ props?: Record<string, any>
86
+ }
87
+
81
88
  export interface TableCellAvatarsOption {
82
89
  image?: string
83
90
  name?: string
@@ -13,3 +13,7 @@ export function isNumber(value: unknown): value is number {
13
13
  export function isArray(value: unknown): value is unknown[] {
14
14
  return Array.isArray(value)
15
15
  }
16
+
17
+ export function isObject(value: unknown): value is Record<string, any> {
18
+ return typeof value === 'object' && value !== null && !isArray(value)
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "Vue Components for Global Brain Design System.",
5
5
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",
6
6
  "license": "MIT",