@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.
- package/lib/components/SDropdownSectionFilter.vue +6 -1
- package/lib/components/SDropdownSectionFilterItemAvatar.vue +12 -3
- package/lib/components/SDropdownSectionFilterItemText.vue +7 -3
- package/lib/components/STableCell.vue +7 -0
- package/lib/composables/Data.ts +108 -0
- package/lib/composables/Form.ts +8 -10
- package/lib/composables/Table.ts +9 -2
- package/lib/support/Utils.ts +4 -0
- package/package.json +1 -1
|
@@ -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
|
-
<
|
|
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:
|
|
21
|
+
display: flex;
|
|
20
22
|
align-items: center;
|
|
21
|
-
padding-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
}
|
|
@@ -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
|
+
}
|
package/lib/composables/Form.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
-
|
|
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,
|
package/lib/composables/Table.ts
CHANGED
|
@@ -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
|
package/lib/support/Utils.ts
CHANGED
|
@@ -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
|
+
}
|