@globalbrain/sefirot 4.11.0 → 4.13.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/components/STooltip.vue +1 -1
- package/lib/composables/Error.ts +3 -1
- package/lib/composables/Lang.ts +3 -3
- package/lib/http/Http.ts +12 -12
- package/lib/styles/variables.css +2 -0
- package/lib/support/Utils.ts +1 -1
- package/package.json +22 -22
- 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>
|
|
@@ -87,7 +87,7 @@ const cleanups = [
|
|
|
87
87
|
setTimeout(() => { ignore.value = false })
|
|
88
88
|
}
|
|
89
89
|
}),
|
|
90
|
-
onClickOutside(root, hide, { ignore: [content] }),
|
|
90
|
+
onClickOutside(root, hide, { ignore: [content], controls: false }),
|
|
91
91
|
() => timeoutId.value != null && window.clearTimeout(timeoutId.value)
|
|
92
92
|
]
|
|
93
93
|
|
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({
|
package/lib/composables/Lang.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type InjectionKey, getCurrentInstance, inject } from 'vue'
|
|
2
2
|
|
|
3
3
|
export type Lang = 'en' | 'ja'
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ export interface HasLang {
|
|
|
15
15
|
lang: Lang
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export const SefirotLangKey = 'sefirot-lang-key'
|
|
18
|
+
export const SefirotLangKey: InjectionKey<Lang> = Symbol.for('sefirot-lang-key')
|
|
19
19
|
|
|
20
20
|
export function useSetupLang(): (user?: HasLang | null) => void {
|
|
21
21
|
const browserLang = useBrowserLang()
|
|
@@ -26,7 +26,7 @@ export function useSetupLang(): (user?: HasLang | null) => void {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export function provideLang(lang: Lang) {
|
|
29
|
-
provide(SefirotLangKey, lang)
|
|
29
|
+
getCurrentInstance()?.appContext.app.provide(SefirotLangKey, lang)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function useLang(): Lang {
|
package/lib/http/Http.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { parse as parseContentDisposition } from '@tinyhttp/content-disposition'
|
|
|
2
2
|
import { parse as parseCookie } from '@tinyhttp/cookie'
|
|
3
3
|
import FileSaver from 'file-saver'
|
|
4
4
|
import { FetchError, type FetchOptions, type FetchRequest, type FetchResponse, ofetch } from 'ofetch'
|
|
5
|
-
import { stringify } from 'qs'
|
|
5
|
+
import { type IStringifyOptions, stringify } from 'qs'
|
|
6
6
|
import { type Lang } from '../composables/Lang'
|
|
7
7
|
import { isBlob, isError, isFormData, isRequest, isResponse, isString } from '../support/Utils'
|
|
8
8
|
|
|
@@ -20,6 +20,7 @@ export interface HttpOptions {
|
|
|
20
20
|
lang?: Lang
|
|
21
21
|
payloadKey?: string
|
|
22
22
|
headers?: () => Awaitable<Record<string, string>>
|
|
23
|
+
stringifyOptions?: IStringifyOptions
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export class Http {
|
|
@@ -29,6 +30,7 @@ export class Http {
|
|
|
29
30
|
private static lang: Lang | undefined = undefined
|
|
30
31
|
private static payloadKey = '__payload__'
|
|
31
32
|
private static headers: () => Awaitable<Record<string, string>> = async () => ({})
|
|
33
|
+
private static stringifyOptions: IStringifyOptions = {}
|
|
32
34
|
|
|
33
35
|
static config(options: HttpOptions): void {
|
|
34
36
|
if (options.baseUrl) {
|
|
@@ -49,6 +51,9 @@ export class Http {
|
|
|
49
51
|
if (options.headers) {
|
|
50
52
|
Http.headers = options.headers
|
|
51
53
|
}
|
|
54
|
+
if (options.stringifyOptions) {
|
|
55
|
+
Http.stringifyOptions = options.stringifyOptions
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
private async ensureXsrfToken(): Promise<string | undefined> {
|
|
@@ -69,7 +74,8 @@ export class Http {
|
|
|
69
74
|
private async buildRequest(url: string, _options: FetchOptions = {}): Promise<[string, FetchOptions]> {
|
|
70
75
|
const { method, params, query, ...options } = _options
|
|
71
76
|
const xsrfToken = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method || '') && (await this.ensureXsrfToken())
|
|
72
|
-
|
|
77
|
+
|
|
78
|
+
const queryString = stringify({ ...params, ...query }, { encodeValuesOnly: true, ...Http.stringifyOptions })
|
|
73
79
|
|
|
74
80
|
return [
|
|
75
81
|
`${url}${queryString ? `?${queryString}` : ''}`,
|
|
@@ -195,16 +201,10 @@ export class Http {
|
|
|
195
201
|
export function isFetchError(e: unknown): e is FetchError {
|
|
196
202
|
return (
|
|
197
203
|
e instanceof FetchError
|
|
198
|
-
|| (isError(e)
|
|
199
|
-
&& (
|
|
200
|
-
&& ((e
|
|
201
|
-
|
|
202
|
-
`[${
|
|
203
|
-
((e as FetchError).request as Request | undefined)?.method || (e as FetchError).options?.method || 'GET'
|
|
204
|
-
}] ${JSON.stringify(
|
|
205
|
-
((e as FetchError).request as Request | undefined)?.url || String((e as FetchError).request) || '/'
|
|
206
|
-
)}: `
|
|
207
|
-
))
|
|
204
|
+
|| (isError<FetchError>(e)
|
|
205
|
+
&& (e.response === undefined || isResponse(e.response))
|
|
206
|
+
&& ((isString(e.request) && e.message.startsWith(`[${e.options?.method || 'GET'}] ${JSON.stringify(e.request || '/')}: `))
|
|
207
|
+
|| (isRequest(e.request) && e.message.startsWith(`[${e.request.method}] ${JSON.stringify(e.request.url)}: `))))
|
|
208
208
|
)
|
|
209
209
|
}
|
|
210
210
|
|
package/lib/styles/variables.css
CHANGED
|
@@ -858,6 +858,8 @@
|
|
|
858
858
|
--input-focus-border-color: var(--c-border-info-1);
|
|
859
859
|
--input-error-text-color: var(--c-text-danger-1);
|
|
860
860
|
--input-error-border-color: var(--c-border-danger-1);
|
|
861
|
+
--input-warning-text-color: var(--c-text-warning-1);
|
|
862
|
+
--input-warning-border-color: var(--c-border-warning-1);
|
|
861
863
|
--input-disabled-border-color: var(--c-border-mute-1);
|
|
862
864
|
--input-disabled-value-color: var(--c-text-1);
|
|
863
865
|
--input-disabled-bg-color: var(--c-bg-mute-1);
|
package/lib/support/Utils.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function isDate(value: unknown): value is Date {
|
|
|
16
16
|
return _isDate(value)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function isError(value: unknown): value is
|
|
19
|
+
export function isError<T extends Error = Error>(value: unknown): value is T {
|
|
20
20
|
return _isError(value)
|
|
21
21
|
}
|
|
22
22
|
|