@globalbrain/sefirot 4.10.0 → 4.12.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/SDescFile.vue +1 -1
- package/lib/components/SDescItem.vue +11 -1
- package/lib/components/SDescLabel.vue +67 -6
- package/lib/components/SInputBase.vue +41 -18
- package/lib/components/SInputDropdown.vue +49 -29
- package/lib/components/SInputDropdownItem.vue +255 -25
- package/lib/components/SInputNumber.vue +16 -25
- package/lib/components/SInputText.vue +19 -23
- package/lib/components/SInputTextarea.vue +19 -23
- package/lib/composables/App.ts +22 -0
- package/lib/composables/Error.ts +3 -1
- package/lib/composables/Http.ts +17 -0
- package/lib/composables/Lang.ts +18 -6
- package/lib/composables/Theme.ts +25 -0
- package/lib/composables/Url.ts +12 -3
- package/lib/styles/variables.css +2 -0
- package/package.json +1 -1
- package/lib/components/SInputDropdownItemAvatar.vue +0 -175
- package/lib/components/SInputDropdownItemText.vue +0 -154
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import IconX from '~icons/ph/x'
|
|
3
|
+
import SAvatar from './SAvatar.vue'
|
|
4
4
|
|
|
5
5
|
export type Item = ItemText | ItemAvatar
|
|
6
6
|
|
|
@@ -23,47 +23,277 @@ export interface ItemAvatar extends ItemBase {
|
|
|
23
23
|
export type Size = 'mini' | 'small' | 'medium'
|
|
24
24
|
|
|
25
25
|
defineProps<{
|
|
26
|
-
|
|
26
|
+
item: Item | Item[]
|
|
27
27
|
size: Size
|
|
28
28
|
removable: boolean
|
|
29
29
|
disabled: boolean
|
|
30
30
|
}>()
|
|
31
31
|
|
|
32
32
|
defineEmits<{
|
|
33
|
-
|
|
33
|
+
remove: [value: any]
|
|
34
34
|
}>()
|
|
35
35
|
</script>
|
|
36
36
|
|
|
37
37
|
<template>
|
|
38
|
-
<div class="SInputDropdownItem">
|
|
39
|
-
<div v-
|
|
40
|
-
<
|
|
41
|
-
v-if="
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
38
|
+
<div class="SInputDropdownItem" :class="[size, { disabled }]">
|
|
39
|
+
<div v-if="Array.isArray(item)" class="many">
|
|
40
|
+
<template v-for="i, index in item" :key="index">
|
|
41
|
+
<div v-if="i.type === undefined || i.type === 'text'" class="many-text">
|
|
42
|
+
<div class="many-text-value">{{ i.label }}</div>
|
|
43
|
+
<button v-if="removable" class="many-text-close" @click.stop="$emit('remove', i.value)">
|
|
44
|
+
<IconX class="many-text-close-icon" />
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
<div v-else-if="i.type === 'avatar'" class="many-avatar">
|
|
48
|
+
<div class="many-avatar-body">
|
|
49
|
+
<div class="many-avatar-image"><SAvatar size="fill" :avatar="i.image" /></div>
|
|
50
|
+
<div class="many-avatar-name">{{ i.label }}</div>
|
|
51
|
+
</div>
|
|
52
|
+
<button v-if="removable" class="many-avatar-close" @click.stop="$emit('remove', i.value)">
|
|
53
|
+
<IconX class="many-avatar-close-icon" />
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
</div>
|
|
58
|
+
<div v-else class="one">
|
|
59
|
+
<div v-if="item.type === undefined || item.type === 'text'" class="one-text">
|
|
60
|
+
<div class="one-text-value">{{ item.label }}</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div v-else-if="item.type === 'avatar'" class="one-avatar">
|
|
63
|
+
<div class="one-avatar-image"><SAvatar size="fill" :avatar="item.image" /></div>
|
|
64
|
+
<div class="one-avatar-name">{{ item.label }}</div>
|
|
65
|
+
</div>
|
|
66
|
+
<button v-if="removable" class="one-close" @click.stop="$emit('remove', item.value)">
|
|
67
|
+
<IconX class="one-close-icon" />
|
|
68
|
+
</button>
|
|
59
69
|
</div>
|
|
60
70
|
</div>
|
|
61
71
|
</template>
|
|
62
72
|
|
|
63
73
|
<style scoped lang="postcss">
|
|
64
74
|
.SInputDropdownItem {
|
|
75
|
+
flex-grow: 1;
|
|
76
|
+
width: 100%;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.many {
|
|
65
80
|
display: flex;
|
|
66
81
|
flex-wrap: wrap;
|
|
67
82
|
gap: 4px;
|
|
68
83
|
}
|
|
84
|
+
|
|
85
|
+
.many-text {
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: 4px;
|
|
89
|
+
border: 1px solid var(--c-divider);
|
|
90
|
+
border-radius: 12px;
|
|
91
|
+
padding: 0 10px;
|
|
92
|
+
height: 24px;
|
|
93
|
+
|
|
94
|
+
&:has(.many-text-close) {
|
|
95
|
+
padding-right: 4px;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.many-text-value {
|
|
100
|
+
line-height: 20px;
|
|
101
|
+
font-size: 12px;
|
|
102
|
+
font-weight: 400;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.many-text-close {
|
|
106
|
+
display: flex;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
align-items: center;
|
|
109
|
+
width: 16px;
|
|
110
|
+
height: 16px;
|
|
111
|
+
color: var(--c-text-2);
|
|
112
|
+
|
|
113
|
+
&:hover {
|
|
114
|
+
color: var(--c-text-1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.many-text-close-icon {
|
|
119
|
+
width: 14px;
|
|
120
|
+
height: 14px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.many-avatar {
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
gap: 4px;
|
|
127
|
+
border: 1px solid var(--c-divider);
|
|
128
|
+
border-radius: 12px;
|
|
129
|
+
padding: 1px 8px 1px 1px;
|
|
130
|
+
height: 24px;
|
|
131
|
+
|
|
132
|
+
&:has(.many-avatar-close) {
|
|
133
|
+
gap: 6px;
|
|
134
|
+
padding-right: 4px;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.many-avatar-body {
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: 6px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.many-avatar-image {
|
|
145
|
+
width: 20px;
|
|
146
|
+
height: 20px;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.many-avatar-name {
|
|
150
|
+
line-height: 20px;
|
|
151
|
+
font-size: 12px;
|
|
152
|
+
font-weight: 400;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.many-avatar-close {
|
|
156
|
+
display: flex;
|
|
157
|
+
justify-content: center;
|
|
158
|
+
align-items: center;
|
|
159
|
+
width: 16px;
|
|
160
|
+
height: 16px;
|
|
161
|
+
color: var(--c-text-2);
|
|
162
|
+
|
|
163
|
+
&:hover {
|
|
164
|
+
color: var(--c-text-1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.many-avatar-close-icon {
|
|
169
|
+
width: 14px;
|
|
170
|
+
height: 14px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.one {
|
|
174
|
+
display: flex;
|
|
175
|
+
align-items: center;
|
|
176
|
+
justify-content: space-between;
|
|
177
|
+
gap: 8px;
|
|
178
|
+
width: 100%;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.one-text {
|
|
182
|
+
flex-grow: 1;
|
|
183
|
+
max-width: 100%;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
white-space: nowrap;
|
|
186
|
+
text-overflow: ellipsis;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.one-text-value {
|
|
190
|
+
max-width: 100%;
|
|
191
|
+
overflow: hidden;
|
|
192
|
+
white-space: nowrap;
|
|
193
|
+
text-overflow: ellipsis;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.one-avatar {
|
|
197
|
+
display: flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
flex-grow: 1;
|
|
200
|
+
max-width: 100%;
|
|
201
|
+
overflow: hidden;
|
|
202
|
+
white-space: nowrap;
|
|
203
|
+
text-overflow: ellipsis;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.one-avatar-image {
|
|
207
|
+
flex-shrink: 0;
|
|
208
|
+
width: 20px;
|
|
209
|
+
height: 20px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.one-avatar-name {
|
|
213
|
+
flex-grow: 1;
|
|
214
|
+
max-width: 100%;
|
|
215
|
+
line-height: 24px;
|
|
216
|
+
font-size: 14px;
|
|
217
|
+
font-weight: 400;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
white-space: nowrap;
|
|
220
|
+
text-overflow: ellipsis;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.one-close {
|
|
224
|
+
display: flex;
|
|
225
|
+
justify-content: center;
|
|
226
|
+
align-items: center;
|
|
227
|
+
flex-shrink: 0;
|
|
228
|
+
width: 16px;
|
|
229
|
+
height: 16px;
|
|
230
|
+
color: var(--c-text-2);
|
|
231
|
+
transition: color 0.25s;
|
|
232
|
+
|
|
233
|
+
&:hover {
|
|
234
|
+
color: var(--c-text-1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.one-close-icon {
|
|
239
|
+
width: 16px;
|
|
240
|
+
height: 16px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.SInputDropdownItem.mini {
|
|
244
|
+
.many {
|
|
245
|
+
padding: 3px 0 3px 3px;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.one-text {
|
|
249
|
+
padding-left: 10px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.one-text-value {
|
|
253
|
+
font-size: var(--input-font-size, var(--input-mini-font-size));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.one-avatar {
|
|
257
|
+
gap: 6px;
|
|
258
|
+
padding-left: 7px;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.SInputDropdownItem.small {
|
|
263
|
+
.many {
|
|
264
|
+
padding: 7px 0 7px 7px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.one-text {
|
|
268
|
+
padding-left: 12px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.one-text-value {
|
|
272
|
+
font-size: var(--input-font-size, var(--input-small-font-size));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.one-avatar {
|
|
276
|
+
gap: 8px;
|
|
277
|
+
padding-left: 10px;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.SInputDropdownItem.medium {
|
|
282
|
+
.many {
|
|
283
|
+
padding: 11px 0 11px 11px;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.one-text {
|
|
287
|
+
padding-left: 16px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.one-text-value {
|
|
291
|
+
font-size: var(--input-font-size, var(--input-medium-font-size));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.one-avatar {
|
|
295
|
+
gap: 8px;
|
|
296
|
+
padding-left: 12px;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
69
299
|
</style>
|
|
@@ -1,41 +1,30 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { type Validatable } from '../composables/Validation'
|
|
2
|
+
import { computed } from 'vue'
|
|
4
3
|
import { isString } from '../support/Utils'
|
|
4
|
+
import { type Props as BaseProps } from './SInputBase.vue'
|
|
5
5
|
import SInputText from './SInputText.vue'
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
export type Align = 'left' | 'center' | 'right'
|
|
9
|
-
export type CheckColor = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
|
|
10
|
-
export type TextColor = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
|
|
11
|
-
|
|
12
|
-
const props = defineProps<{
|
|
13
|
-
size?: Size
|
|
14
|
-
name?: string
|
|
15
|
-
label?: string
|
|
16
|
-
info?: string
|
|
17
|
-
note?: string
|
|
18
|
-
help?: string
|
|
7
|
+
export interface Props extends BaseProps {
|
|
19
8
|
placeholder?: string
|
|
20
9
|
unitBefore?: any
|
|
21
10
|
unitAfter?: any
|
|
22
|
-
checkIcon?: Component
|
|
23
|
-
checkText?: string
|
|
24
|
-
checkColor?: CheckColor
|
|
25
11
|
textColor?: TextColor | ((value: number | null) => TextColor)
|
|
26
|
-
align?: Align
|
|
27
12
|
separator?: boolean
|
|
13
|
+
align?: Align
|
|
28
14
|
disabled?: boolean
|
|
29
15
|
value?: number | null
|
|
30
16
|
modelValue?: number | null
|
|
31
17
|
displayValue?: string | null
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type Align = 'left' | 'center' | 'right'
|
|
21
|
+
export type TextColor = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
|
|
22
|
+
|
|
23
|
+
const props = defineProps<Props>()
|
|
35
24
|
|
|
36
25
|
const emit = defineEmits<{
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
'update:model-value': [value: number | null]
|
|
27
|
+
'input': [value: number | null]
|
|
39
28
|
}>()
|
|
40
29
|
|
|
41
30
|
const _value = computed(() => {
|
|
@@ -86,8 +75,8 @@ function emitUpdate(value: string | null) {
|
|
|
86
75
|
<template>
|
|
87
76
|
<SInputText
|
|
88
77
|
class="SInputNumber"
|
|
89
|
-
:name="name"
|
|
90
78
|
:size="size"
|
|
79
|
+
:name="name"
|
|
91
80
|
type="number"
|
|
92
81
|
:label="label"
|
|
93
82
|
:note="note"
|
|
@@ -102,10 +91,12 @@ function emitUpdate(value: string | null) {
|
|
|
102
91
|
:text-color="_textColor"
|
|
103
92
|
:align="align"
|
|
104
93
|
:disabled="disabled"
|
|
105
|
-
:hide-error="hideError"
|
|
106
94
|
:display-value="displayValue"
|
|
107
95
|
:model-value="_value == null ? null : String(_value)"
|
|
108
96
|
:validation="validation"
|
|
97
|
+
:warning="warning"
|
|
98
|
+
:hide-error="hideError"
|
|
99
|
+
:hide-warning="hideWarning"
|
|
109
100
|
@update:model-value="emitUpdate"
|
|
110
101
|
@input="emitUpdate"
|
|
111
102
|
>
|
|
@@ -1,51 +1,36 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { type Component, computed, ref } from 'vue'
|
|
3
|
-
import { type Validatable } from '../composables/Validation'
|
|
4
3
|
import { isString } from '../support/Utils'
|
|
5
|
-
import SInputBase from './SInputBase.vue'
|
|
6
|
-
|
|
7
|
-
export interface Props {
|
|
8
|
-
size?: Size
|
|
9
|
-
name?: string
|
|
10
|
-
label?: string
|
|
11
|
-
info?: string
|
|
12
|
-
note?: string
|
|
13
|
-
help?: string
|
|
4
|
+
import SInputBase, { type Props as BaseProps } from './SInputBase.vue'
|
|
5
|
+
|
|
6
|
+
export interface Props extends BaseProps {
|
|
14
7
|
type?: string
|
|
15
8
|
placeholder?: string
|
|
16
9
|
unitBefore?: Component | string
|
|
17
10
|
unitAfter?: Component | string
|
|
18
|
-
checkIcon?: Component
|
|
19
|
-
checkText?: string
|
|
20
|
-
checkColor?: CheckColor
|
|
21
11
|
textColor?: TextColor | ((value: string | null) => TextColor)
|
|
22
12
|
align?: Align
|
|
23
13
|
disabled?: boolean
|
|
24
14
|
modelValue: string | null
|
|
25
15
|
displayValue?: string | null
|
|
26
|
-
hideError?: boolean
|
|
27
|
-
validation?: Validatable
|
|
28
16
|
}
|
|
29
17
|
|
|
30
|
-
export type Size = 'mini' | 'small' | 'medium'
|
|
31
18
|
export type Align = 'left' | 'center' | 'right'
|
|
32
|
-
export type CheckColor = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
|
|
33
19
|
export type TextColor = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
|
|
34
20
|
|
|
35
21
|
const props = defineProps<Props>()
|
|
36
22
|
|
|
37
23
|
const emit = defineEmits<{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
24
|
+
'update:model-value': [value: string | null]
|
|
25
|
+
'input': [value: string | null]
|
|
26
|
+
'blur': [value: string | null]
|
|
27
|
+
'enter': [value: string | null]
|
|
42
28
|
}>()
|
|
43
29
|
|
|
44
30
|
const input = ref<HTMLElement | null>(null)
|
|
45
31
|
const isFocused = ref(false)
|
|
46
32
|
|
|
47
33
|
const classes = computed(() => [
|
|
48
|
-
props.size ?? 'small',
|
|
49
34
|
props.align ?? 'left',
|
|
50
35
|
{ disabled: props.disabled }
|
|
51
36
|
])
|
|
@@ -116,6 +101,7 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
|
|
|
116
101
|
<SInputBase
|
|
117
102
|
class="SInputText"
|
|
118
103
|
:class="classes"
|
|
104
|
+
:size="size"
|
|
119
105
|
:name="name"
|
|
120
106
|
:label="label"
|
|
121
107
|
:note="note"
|
|
@@ -124,8 +110,10 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
|
|
|
124
110
|
:check-icon="checkIcon"
|
|
125
111
|
:check-text="checkText"
|
|
126
112
|
:check-color="checkColor"
|
|
127
|
-
:hide-error="hideError"
|
|
128
113
|
:validation="validation"
|
|
114
|
+
:warning="warning"
|
|
115
|
+
:hide-error="hideError"
|
|
116
|
+
:hide-warning="hideWarning"
|
|
129
117
|
>
|
|
130
118
|
<div class="box" :class="{ focus: isFocused }" @click="focus">
|
|
131
119
|
<div v-if="$slots['addon-before']" class="addon before">
|
|
@@ -349,6 +337,14 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
|
|
|
349
337
|
}
|
|
350
338
|
}
|
|
351
339
|
|
|
340
|
+
.SInputText.has-warning {
|
|
341
|
+
.box,
|
|
342
|
+
.box:hover,
|
|
343
|
+
.box:focus {
|
|
344
|
+
border-color: var(--input-warning-border-color);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
352
348
|
.box {
|
|
353
349
|
position: relative;
|
|
354
350
|
display: flex;
|
|
@@ -1,42 +1,29 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { type
|
|
4
|
-
import SInputBase from './SInputBase.vue'
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
3
|
+
import SInputBase, { type Props as BaseProps } from './SInputBase.vue'
|
|
5
4
|
import SInputSegments from './SInputSegments.vue'
|
|
6
5
|
|
|
7
|
-
export
|
|
8
|
-
export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
|
|
9
|
-
|
|
10
|
-
const props = withDefaults(defineProps<{
|
|
11
|
-
size?: Size
|
|
12
|
-
name?: string
|
|
13
|
-
label?: string
|
|
14
|
-
info?: string
|
|
15
|
-
note?: string
|
|
16
|
-
help?: string
|
|
17
|
-
checkIcon?: Component
|
|
18
|
-
checkText?: string
|
|
19
|
-
checkColor?: Color
|
|
6
|
+
export interface Props extends BaseProps {
|
|
20
7
|
placeholder?: string
|
|
21
8
|
disabled?: boolean
|
|
22
9
|
rows?: number | 'fill'
|
|
23
10
|
autoResize?: boolean | number
|
|
24
11
|
value?: string | null
|
|
25
12
|
modelValue?: string | null
|
|
26
|
-
hideError?: boolean
|
|
27
|
-
validation?: Validatable
|
|
28
13
|
preview?: (value: string | null) => string
|
|
29
14
|
previewLabel?: string
|
|
30
15
|
writeLabel?: string
|
|
31
|
-
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
32
19
|
size: 'small',
|
|
33
20
|
rows: 3
|
|
34
21
|
})
|
|
35
22
|
|
|
36
23
|
const emit = defineEmits<{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
'update:model-value': [value: string | null]
|
|
25
|
+
'input': [value: string | null]
|
|
26
|
+
'blur': [value: string | null]
|
|
40
27
|
}>()
|
|
41
28
|
|
|
42
29
|
const sizePaddingYDict = {
|
|
@@ -93,6 +80,7 @@ const isPreview = ref(false)
|
|
|
93
80
|
<SInputBase
|
|
94
81
|
class="SInputTextarea"
|
|
95
82
|
:class="classes"
|
|
83
|
+
:size="size"
|
|
96
84
|
:name="name"
|
|
97
85
|
:label="label"
|
|
98
86
|
:note="note"
|
|
@@ -101,8 +89,10 @@ const isPreview = ref(false)
|
|
|
101
89
|
:check-icon="checkIcon"
|
|
102
90
|
:check-text="checkText"
|
|
103
91
|
:check-color="checkColor"
|
|
104
|
-
:hide-error="hideError"
|
|
105
92
|
:validation="validation"
|
|
93
|
+
:warning="warning"
|
|
94
|
+
:hide-error="hideError"
|
|
95
|
+
:hide-warning="hideWarning"
|
|
106
96
|
>
|
|
107
97
|
<div class="box">
|
|
108
98
|
<div v-if="preview !== undefined" class="control">
|
|
@@ -248,4 +238,10 @@ const isPreview = ref(false)
|
|
|
248
238
|
border-color: var(--input-error-border-color);
|
|
249
239
|
}
|
|
250
240
|
}
|
|
241
|
+
|
|
242
|
+
.SInputTextarea.has-warning {
|
|
243
|
+
.box {
|
|
244
|
+
border-color: var(--input-warning-border-color);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
251
247
|
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type HttpOptions } from '../http/Http'
|
|
2
|
+
import { useSetupHttp } from './Http'
|
|
3
|
+
import { type HasLang, useSetupLang } from './Lang'
|
|
4
|
+
import { type HasTheme, useSetupTheme } from './Theme'
|
|
5
|
+
|
|
6
|
+
export interface SetupAppUser extends HasLang, HasTheme {}
|
|
7
|
+
|
|
8
|
+
export interface SetupAppOptions {
|
|
9
|
+
http?: HttpOptions
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useSetupApp(): (user?: SetupAppUser | null, options?: SetupAppOptions) => void {
|
|
13
|
+
const setupLang = useSetupLang()
|
|
14
|
+
const setupTheme = useSetupTheme()
|
|
15
|
+
const setupHttp = useSetupHttp()
|
|
16
|
+
|
|
17
|
+
return (user, options) => {
|
|
18
|
+
setupLang(user)
|
|
19
|
+
setupTheme(user)
|
|
20
|
+
setupHttp(user, options?.http)
|
|
21
|
+
}
|
|
22
|
+
}
|
package/lib/composables/Error.ts
CHANGED
|
@@ -143,7 +143,9 @@ function formatProps(props: Record<string, unknown>): string {
|
|
|
143
143
|
const ignoreErrors = [
|
|
144
144
|
/Network Error/,
|
|
145
145
|
/Non-Error (?:exception|promise rejection) captured/,
|
|
146
|
-
/ResizeObserver loop
|
|
146
|
+
/ResizeObserver loop/,
|
|
147
|
+
/Can't find variable: gmo/,
|
|
148
|
+
/\[Cloudflare Turnstile\] Error: (?:10[2-46]|1106[02]|[36]00)/
|
|
147
149
|
]
|
|
148
150
|
|
|
149
151
|
export function useErrorHandler({
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Lang, useBrowserLang } from 'sefirot/composables/Lang'
|
|
2
|
+
import { Http, type HttpOptions } from 'sefirot/http/Http'
|
|
3
|
+
|
|
4
|
+
export interface HasLang {
|
|
5
|
+
lang: Lang
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function useSetupHttp(): (user?: HasLang | null, options?: HttpOptions) => void {
|
|
9
|
+
const browserLang = useBrowserLang()
|
|
10
|
+
|
|
11
|
+
return (user, options = {}) => {
|
|
12
|
+
Http.config({
|
|
13
|
+
lang: user?.lang ?? browserLang,
|
|
14
|
+
...options
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
}
|
package/lib/composables/Lang.ts
CHANGED
|
@@ -11,8 +11,20 @@ export interface TransMessages<T> {
|
|
|
11
11
|
ja: T
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export interface HasLang {
|
|
15
|
+
lang: Lang
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
export const SefirotLangKey = 'sefirot-lang-key'
|
|
15
19
|
|
|
20
|
+
export function useSetupLang(): (user?: HasLang | null) => void {
|
|
21
|
+
const browserLang = useBrowserLang()
|
|
22
|
+
|
|
23
|
+
return (user) => {
|
|
24
|
+
provideLang(user?.lang ?? browserLang)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
export function provideLang(lang: Lang) {
|
|
17
29
|
provide(SefirotLangKey, lang)
|
|
18
30
|
}
|
|
@@ -23,6 +35,12 @@ export function useLang(): Lang {
|
|
|
23
35
|
return inject(SefirotLangKey, 'en') || 'en'
|
|
24
36
|
}
|
|
25
37
|
|
|
38
|
+
export function useBrowserLang(): Lang {
|
|
39
|
+
const lang = navigator.language
|
|
40
|
+
|
|
41
|
+
return lang.split('-')[0] === 'ja' ? 'ja' : 'en'
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
export function useTrans<T>(messages: TransMessages<T>): Trans<T> {
|
|
27
45
|
const lang = useLang()
|
|
28
46
|
|
|
@@ -32,9 +50,3 @@ export function useTrans<T>(messages: TransMessages<T>): Trans<T> {
|
|
|
32
50
|
t
|
|
33
51
|
}
|
|
34
52
|
}
|
|
35
|
-
|
|
36
|
-
export function useBrowserLang(): Lang {
|
|
37
|
-
const lang = navigator.language
|
|
38
|
-
|
|
39
|
-
return lang.split('-')[0] === 'ja' ? 'ja' : 'en'
|
|
40
|
-
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useDark } from '@vueuse/core'
|
|
2
|
+
import { type WritableComputedRef, computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
export type Theme = 'light' | 'dark'
|
|
5
|
+
|
|
6
|
+
export interface HasTheme {
|
|
7
|
+
theme: Theme
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function useSetupTheme(): (user?: HasTheme | null) => void {
|
|
11
|
+
const theme = useTheme()
|
|
12
|
+
|
|
13
|
+
return (user) => {
|
|
14
|
+
theme.value = user?.theme ?? 'light'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function useTheme(): WritableComputedRef<Theme> {
|
|
19
|
+
const _isDark = useDark()
|
|
20
|
+
|
|
21
|
+
return computed({
|
|
22
|
+
get: () => _isDark.value ? 'dark' : 'light',
|
|
23
|
+
set: (v) => { _isDark.value = v === 'dark' }
|
|
24
|
+
})
|
|
25
|
+
}
|