@globalbrain/sefirot 2.1.4 → 2.2.1

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.
@@ -83,6 +83,14 @@ const hasSelected = computed(() => {
83
83
  return selected.value.length > 0
84
84
  })
85
85
 
86
+ const removable = computed(() => {
87
+ if (isArray(props.modelValue)) {
88
+ return props.nullable || selected.value.length > 1
89
+ }
90
+
91
+ return !!props.nullable
92
+ })
93
+
86
94
  async function handleOpen() {
87
95
  !props.disabled && open()
88
96
  }
@@ -137,6 +145,7 @@ function handleArray(value: OptionValue) {
137
145
  <SInputDropdownItem
138
146
  v-if="hasSelected"
139
147
  :items="selected"
148
+ :removable="removable"
140
149
  :disabled="disabled ?? false"
141
150
  @remove="handleSelect"
142
151
  />
@@ -223,8 +232,8 @@ function handleArray(value: OptionValue) {
223
232
  }
224
233
  }
225
234
 
226
- .SInputSelect.has-error {
227
- .select {
235
+ .SInputDropdown.has-error {
236
+ .box {
228
237
  border-color: var(--c-danger);
229
238
  }
230
239
  }
@@ -266,6 +275,8 @@ function handleArray(value: OptionValue) {
266
275
  padding: 2px 4px;
267
276
  font-weight: 500;
268
277
  color: var(--c-text-3);
278
+ overflow: hidden;
279
+ white-space: nowrap;
269
280
  }
270
281
 
271
282
  .box-icon {
@@ -22,6 +22,7 @@ export interface ItemAvatar extends ItemBase {
22
22
 
23
23
  defineProps<{
24
24
  items: Item[]
25
+ removable: boolean
25
26
  disabled: boolean
26
27
  }>()
27
28
 
@@ -37,6 +38,7 @@ defineEmits<{
37
38
  v-if="item.type === 'text' || item.type === undefined"
38
39
  :label="item.label"
39
40
  :value="item.value"
41
+ :removable="removable"
40
42
  :disabled="disabled"
41
43
  @remove="(v) => $emit('remove', v)"
42
44
  />
@@ -45,6 +47,7 @@ defineEmits<{
45
47
  :label="item.label"
46
48
  :image="item.image"
47
49
  :value="item.value"
50
+ :removable="removable"
48
51
  :disabled="disabled"
49
52
  @remove="(v) => $emit('remove', v)"
50
53
  />
@@ -7,6 +7,7 @@ defineProps<{
7
7
  label: string
8
8
  image?: string | null
9
9
  value: string | number | boolean
10
+ removable: boolean
10
11
  disabled: boolean
11
12
  }>()
12
13
 
@@ -16,7 +17,7 @@ defineEmits<{
16
17
  </script>
17
18
 
18
19
  <template>
19
- <div class="SInputDropdownItemAvatar" :class="{ disabled }">
20
+ <div class="SInputDropdownItemAvatar" :class="{ disabled, removable }">
20
21
  <div class="user">
21
22
  <div class="avatar">
22
23
  <SAvatar size="nano" :avatar="image" :name="label" />
@@ -24,7 +25,7 @@ defineEmits<{
24
25
  <p class="name">{{ label }}</p>
25
26
  </div>
26
27
 
27
- <div v-if="!disabled" class="remove" role="button" @click="$emit('remove', value)">
28
+ <div v-if="!disabled && removable" class="remove" role="button" @click="$emit('remove', value)">
28
29
  <div class="remove-box">
29
30
  <SIcon :icon="IconX" class="remove-icon" />
30
31
  </div>
@@ -37,10 +38,14 @@ defineEmits<{
37
38
  display: flex;
38
39
  border: 1px solid var(--c-divider-light);
39
40
  border-radius: 14px;
40
- padding: 0;
41
+ padding: 0 12px 0 0;
41
42
  background-color: var(--c-bg-mute);
42
43
  }
43
44
 
45
+ .SInputDropdownItemAvatar.removable {
46
+ padding: 0;
47
+ }
48
+
44
49
  .SInputDropdownItemUserAvatar.disabled {
45
50
  padding: 0 10px 0 0;
46
51
  background-color: var(--c-gray-light-4);
@@ -5,6 +5,7 @@ import SIcon from './SIcon.vue'
5
5
  defineProps<{
6
6
  label: string
7
7
  value: string | number | boolean
8
+ removable: boolean
8
9
  disabled: boolean
9
10
  }>()
10
11
 
@@ -14,10 +15,10 @@ defineEmits<{
14
15
  </script>
15
16
 
16
17
  <template>
17
- <div class="SInputDropdownItemText" :class="{ disabled }">
18
+ <div class="SInputDropdownItemText" :class="{ disabled, removable }">
18
19
  <p class="text">{{ label }}</p>
19
20
 
20
- <div v-if="!disabled" class="remove" role="button" @click.stop="$emit('remove', value)">
21
+ <div v-if="!disabled && removable" class="remove" role="button" @click.stop="$emit('remove', value)">
21
22
  <div class="remove-box">
22
23
  <SIcon :icon="IconX" class="remove-icon" />
23
24
  </div>
@@ -30,12 +31,16 @@ defineEmits<{
30
31
  display: flex;
31
32
  border: 1px solid var(--c-divider-light);
32
33
  border-radius: 14px;
33
- padding: 0 0 0 12px;
34
+ padding: 0 12px;
34
35
  background-color: var(--c-bg-mute);
35
36
  }
36
37
 
38
+ .SInputDropdownItemText.removable {
39
+ padding: 0 0 0 12px;
40
+ }
41
+
37
42
  .SInputDropdownItemText.disabled {
38
- padding: 0 10px 0;
43
+ padding: 0 10px;
39
44
  background-color: var(--c-gray-light-4);
40
45
 
41
46
  .text {
@@ -1,56 +1,35 @@
1
- <template>
2
- <SInputText
3
- class="SInputNumber"
4
- :name="name"
5
- :size="size"
6
- type="number"
7
- :label="label"
8
- :note="note"
9
- :help="help"
10
- :align="align"
11
- :placeholder="placeholder"
12
- :disabled="disabled"
13
- :error-message="errorMessage"
14
- :display-value="displayValue"
15
- :model-value="String(modelValue)"
16
- :validation="validation"
17
- @update:model-value="emitUpdate"
18
- >
19
- <template #before-help>
20
- <p v-if="helpFormat" class="help-text">
21
- {{ valueWithSeparator }}
22
- </p>
23
- </template>
24
- </SInputText>
25
- </template>
26
-
27
1
  <script setup lang="ts">
28
- import { computed, PropType } from 'vue'
2
+ import { computed } from 'vue'
29
3
  import { Validatable } from '../composables/Validation'
30
4
  import { isNullish } from '../support/Utils'
31
- import SInputText, { Size, Align } from './SInputText.vue'
5
+ import SInputText from './SInputText.vue'
32
6
 
33
- const props = defineProps({
34
- size: { type: String as PropType<Size>, default: 'small' },
35
- name: { type: String, default: null },
36
- label: { type: String, default: null },
37
- note: { type: String, default: null },
38
- help: { type: String, default: null },
39
- placeholder: { type: String, default: null },
40
- align: { type: String as PropType<Align>, default: null },
41
- separator: { type: Boolean, default: false },
42
- helpFormat: { type: Boolean, default: false },
43
- disabled: { type: Boolean, default: false },
44
- errorMessage: { type: Boolean, default: true },
45
- modelValue: { type: Number as PropType<number | null>, default: null },
46
- validation: { type: Object as PropType<Validatable>, default: null }
47
- })
7
+ export type Size = 'mini' | 'small' | 'medium'
8
+ export type Align = 'left' | 'center' | 'right'
9
+
10
+ const props = defineProps<{
11
+ size?: Size
12
+ name?: string
13
+ label?: string
14
+ note?: string
15
+ help?: string
16
+ placeholder?: string
17
+ align?: Align
18
+ separator?: boolean
19
+ disabled?: boolean
20
+ modelValue: number | null
21
+ displayValue?: string | null
22
+ hideError?: boolean
23
+ validation?: Validatable
24
+ }>()
48
25
 
49
- const emit = defineEmits(['update:modelValue'])
26
+ const emit = defineEmits<{
27
+ (e: 'update:modelValue', value: number | null): void
28
+ }>()
50
29
 
51
30
  const valueWithSeparator = computed(() => {
52
31
  if (isNullish(props.modelValue)) {
53
- return '0'
32
+ return null
54
33
  }
55
34
 
56
35
  return props.modelValue >= 100000000000000000000
@@ -59,14 +38,36 @@ const valueWithSeparator = computed(() => {
59
38
  })
60
39
 
61
40
  const displayValue = computed(() => {
62
- if (!props.separator || valueWithSeparator.value === '0') {
63
- return null
41
+ if (!isNullish(props.displayValue)) {
42
+ return props.displayValue
64
43
  }
65
44
 
66
- return valueWithSeparator.value
45
+ return !props.separator || valueWithSeparator.value === null
46
+ ? null
47
+ : valueWithSeparator.value
67
48
  })
68
49
 
69
- function emitUpdate(value: string | null): void {
70
- emit('update:modelValue', value ? Number(value) : null)
50
+ function emitUpdate(value: string | null) {
51
+ emit('update:modelValue', isNullish(value) ? null : Number(value))
71
52
  }
72
53
  </script>
54
+
55
+ <template>
56
+ <SInputText
57
+ class="SInputNumber"
58
+ :name="name"
59
+ :size="size"
60
+ type="number"
61
+ :label="label"
62
+ :note="note"
63
+ :help="help"
64
+ :align="align"
65
+ :placeholder="placeholder"
66
+ :disabled="disabled"
67
+ :hide-error="hideError"
68
+ :display-value="displayValue"
69
+ :model-value="String(modelValue)"
70
+ :validation="validation"
71
+ @update:model-value="emitUpdate"
72
+ />
73
+ </template>
@@ -141,6 +141,10 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
141
141
  font-size: 14px;
142
142
  }
143
143
 
144
+ .display {
145
+ top: 0;
146
+ }
147
+
144
148
  .icon {
145
149
  width: 22px;
146
150
  height: 30px;
@@ -172,6 +176,10 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
172
176
  font-size: 16px;
173
177
  }
174
178
 
179
+ .display {
180
+ top: 2px;
181
+ }
182
+
175
183
  .icon {
176
184
  width: 26px;
177
185
  height: 38px;
@@ -203,6 +211,10 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
203
211
  font-size: 16px;
204
212
  }
205
213
 
214
+ .display {
215
+ top: 0;
216
+ }
217
+
206
218
  .icon {
207
219
  width: 28px;
208
220
  height: 46px;
@@ -288,7 +300,6 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
288
300
 
289
301
  .display {
290
302
  position: absolute;
291
- top: 0;
292
303
  left: 0;
293
304
  width: 100%;
294
305
  }
@@ -98,7 +98,7 @@ function createRequiredTouched(): boolean[] {
98
98
  <template>
99
99
  <SInputBase
100
100
  class="SInputYMD"
101
- :class="[size, { disabled }]"
101
+ :class="[size ?? 'small', { disabled }]"
102
102
  :label="label"
103
103
  :note="note"
104
104
  :help="help"
@@ -1,12 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
- import type { TableCellPillColor } from '../composables/Table'
3
+
4
+ export type Color = 'info' | 'success' | 'warning' | 'danger' | 'mute'
4
5
 
5
6
  const props = defineProps<{
6
7
  value?: any
7
8
  record: any
8
9
  getter?: string | ((value: any) => string)
9
- color?: TableCellPillColor | ((value: any) => TableCellPillColor)
10
+ color?: Color | ((value: any) => Color)
10
11
  }>()
11
12
 
12
13
  const _value = computed(() => {
@@ -14,7 +14,7 @@ export interface Form<T extends Record<string, any>> {
14
14
 
15
15
  export interface UseFormOptions<T extends Record<string, any>> {
16
16
  data: T,
17
- rules?: Record<string, any>
17
+ rules?: Record<string, any> | ((state: T) => Record<string, any>)
18
18
  }
19
19
 
20
20
  export function useForm<
@@ -26,7 +26,9 @@ export function useForm<
26
26
 
27
27
  const data = reactive(options.data)
28
28
 
29
- const rules = options.rules ?? {}
29
+ const rules = options.rules
30
+ ? typeof options.rules === 'function' ? options.rules(data) : options.rules
31
+ : {}
30
32
 
31
33
  const validation = useValidation(data, rules)
32
34
 
@@ -1,12 +1,18 @@
1
+ export { or, and, not } from '@vuelidate/validators'
2
+
1
3
  export * from './checked'
2
4
  export * from './email'
3
5
  export * from './fileExtension'
4
6
  export * from './hms'
5
7
  export * from './maxLength'
6
8
  export * from './minLength'
9
+ export * from './maxValue'
10
+ export * from './minValue'
7
11
  export * from './required'
8
12
  export * from './requiredHms'
9
13
  export * from './requiredIf'
10
14
  export * from './requiredYmd'
11
15
  export * from './url'
16
+ export * from './month'
12
17
  export * from './ymd'
18
+ export * from './rule'
@@ -0,0 +1,10 @@
1
+ import { helpers, maxValue as baseMaxValue } from '@vuelidate/validators'
2
+
3
+ export function maxValue(value: number, msg?: string) {
4
+ return helpers.withMessage(
5
+ ({ $params }) => {
6
+ return msg ?? `The value must be less or equal to ${($params as any).max}.`
7
+ },
8
+ baseMaxValue(value)
9
+ )
10
+ }
@@ -0,0 +1,10 @@
1
+ import { helpers, minValue as baseMinValue } from '@vuelidate/validators'
2
+
3
+ export function minValue(value: number, msg?: string) {
4
+ return helpers.withMessage(
5
+ ({ $params }) => {
6
+ return msg ?? `The value must be greater or equal to ${($params as any).min}.`
7
+ },
8
+ baseMinValue(value)
9
+ )
10
+ }
@@ -0,0 +1,11 @@
1
+ import { helpers } from '@vuelidate/validators'
2
+ import { month as baseMonth } from '../validators/month'
3
+
4
+ export function month(msg?: string) {
5
+ return helpers.withMessage(
6
+ () => msg ?? 'The month is invalid.',
7
+ (value: number) => {
8
+ return !helpers.req(value) || baseMonth(value)
9
+ }
10
+ )
11
+ }
@@ -0,0 +1,8 @@
1
+ import { helpers } from '@vuelidate/validators'
2
+
3
+ export function rule(validation: (value: any) => boolean, msg?: string) {
4
+ return helpers.withMessage(
5
+ () => msg ?? 'The value is invalid.',
6
+ (value: any) => !helpers.req(value) || validation(value)
7
+ )
8
+ }
@@ -0,0 +1,3 @@
1
+ export function month(value: number): boolean {
2
+ return value > 0 && value < 13
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "2.1.4",
3
+ "version": "2.2.1",
4
4
  "description": "Vue Components for Global Brain Design System.",
5
5
  "files": [
6
6
  "lib"
@@ -84,7 +84,7 @@
84
84
  "vitest": "^0.23.4",
85
85
  "vue": "^3.2.40",
86
86
  "vue-router": "^4.1.5",
87
- "vue-tsc": "^0.40.13"
87
+ "vue-tsc": "^1.0.8"
88
88
  },
89
89
  "scripts": {
90
90
  "docs": "vitepress dev docs --port 3000",