@globalbrain/sefirot 3.4.0 → 3.6.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/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # Sefirot
2
2
 
3
3
  [![GitHub Actions](https://github.com/globalbrain/sefirot/workflows/Test/badge.svg)](https://github.com/globalbrain/sefirot/actions)
4
- [![Codecov](https://codecov.io/gh/globalbrain/sefirot/branch/main/graph/badge.svg)](https://codecov.io/gh/globalbrain/sefirot)
5
4
  [![License](https://img.shields.io/npm/l/@globalbrain/sefirot.svg)](https://github.com/globalbrain/sefirot/blob/main/LICENSE.md)
6
5
 
7
6
  Sefirot is a collection of Vue Components for Global Brain Design System. Components are meant to be clean, sophisticated, and scalable.
@@ -0,0 +1,98 @@
1
+ <script setup lang="ts">
2
+ import IconDownloadSimple from '@iconify-icons/ph/download-simple-bold'
3
+ import IconFileText from '@iconify-icons/ph/file-text-bold'
4
+ import { computed } from 'vue'
5
+ import { isArray } from '../support/Utils'
6
+ import SButton from './SButton.vue'
7
+ import SDescEmpty from './SDescEmpty.vue'
8
+ import SIcon from './SIcon.vue'
9
+
10
+ export interface Item {
11
+ name: string
12
+ onDownload(): void
13
+ }
14
+
15
+ const props = defineProps<{
16
+ item?: Item | Item[] | null
17
+ }>()
18
+
19
+ const items = computed(() => {
20
+ return props.item
21
+ ? isArray(props.item) ? props.item : [props.item]
22
+ : null
23
+ })
24
+ </script>
25
+
26
+ <template>
27
+ <div v-if="items && items.length" class="SDescFile">
28
+ <div class="value">
29
+ <div v-for="item, index in items" :key="index" class="item">
30
+ <div class="data">
31
+ <div class="icon"><SIcon class="icon-svg" :icon="IconFileText" /></div>
32
+ <div class="name">{{ item.name }}</div>
33
+ </div>
34
+ <div class="actions">
35
+ <SButton
36
+ size="small"
37
+ type="text"
38
+ mode="info"
39
+ :icon="IconDownloadSimple"
40
+ label="Download"
41
+ @click="item.onDownload"
42
+ />
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ <SDescEmpty v-else />
48
+ </template>
49
+
50
+ <style scoped lang="postcss">
51
+ .value {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 1px;
55
+ border: 1px solid var(--c-divider);
56
+ border-radius: 6px;
57
+ margin-top: 2px;
58
+ background-color: var(--c-gutter);
59
+ overflow: hidden;
60
+ }
61
+
62
+ .item {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 8px;
66
+ padding: 0 8px 0 12px;
67
+ width: 100%;
68
+ height: 48px;
69
+ background-color: var(--c-bg-elv-4);
70
+ }
71
+
72
+ .data {
73
+ display: flex;
74
+ gap: 8px;
75
+ flex-grow: 1;
76
+ align-items: center;
77
+ overflow: hidden;
78
+ }
79
+
80
+ .icon-svg {
81
+ width: 18px;
82
+ height: 18px;
83
+ color: var(--c-text-2);
84
+ }
85
+
86
+ .name {
87
+ line-height: 24px;
88
+ font-size: 14px;
89
+ color: var(--c-text-1);
90
+ white-space: nowrap;
91
+ overflow: hidden;
92
+ text-overflow: ellipsis;
93
+ }
94
+
95
+ .actions {
96
+ flex-shrink: 0;
97
+ }
98
+ </style>
@@ -25,13 +25,14 @@ const labelWidth = computed(() => {
25
25
  <style scoped lang="postcss">
26
26
  .SDescItem {
27
27
  display: grid;
28
+ grid-template-columns: minmax(0, 1fr);
28
29
  }
29
30
 
30
31
  .SDesc.row > .SDescItem {
31
- grid-template-columns: var(--desc-label-width, v-bind(labelWidth)) 1fr;
32
+ grid-template-columns: var(--desc-label-width, v-bind(labelWidth)) minmax(0, 1fr);
32
33
  }
33
34
 
34
- .SDesc.divider > .SDescItem {
35
+ .SDesc.divider > .SDescItem:not(:has(> .SDescFile)) {
35
36
  border-bottom: 1px dashed var(--c-divider);
36
37
  padding-bottom: 7px;
37
38
  }
@@ -60,11 +60,13 @@ function getErrorMsg(validation: Validatable) {
60
60
  <span class="label-text">{{ label }}</span>
61
61
 
62
62
  <STooltip v-if="hasInfo" :text="info" trigger="focus" @click.prevent>
63
- <SIcon class="label-info" :icon="IconQuestion" />
63
+ <div class="label-info">
64
+ <SIcon class="label-info-icon" :icon="IconQuestion" />
65
+ </div>
64
66
  <template v-if="$slots.info" #text><slot name="info" /></template>
65
67
  </STooltip>
66
68
 
67
- <span class="label-note">{{ note }}</span>
69
+ <span class="label-note" :class="{ 'has-info': hasInfo }">{{ note }}</span>
68
70
 
69
71
  <span v-if="checkIcon || checkText" class="check" :class="checkColor || 'neutral'">
70
72
  <SIcon v-if="checkIcon" class="check-icon" :icon="checkIcon" />
@@ -84,18 +86,21 @@ function getErrorMsg(validation: Validatable) {
84
86
 
85
87
  <style scoped lang="postcss">
86
88
  .SInputBase.mini {
87
- .label { padding-bottom: 6px; height: 22px; }
88
- .label-text { font-size: var(--input-label-font-size, var(--input-mini-label-font-size)); }
89
- .label-info { width: 14px; height: 14px; }
89
+ .label { padding-bottom: 4px; min-height: 24px; }
90
+ .label-text { font-size: var(--input-label-font-size, var(--input-mini-label-font-size)); }
91
+ .label-info { margin-top: 0; margin-bottom: 0; width: 20px; height: 20px; }
92
+ .label-info-icon { width: 14px; height: 14px; }
93
+ .label-note { padding-top: 0; line-height: 20px; }
94
+ .check { padding-top: 0; line-height: 20px; }
90
95
  }
91
96
 
92
97
  .SInputBase.small {
93
- .label { padding-bottom: 8px; height: 24px; }
98
+ .label { padding-bottom: 6px; min-height: 26px; }
94
99
  .label-text { font-size: var(--input-label-font-size, var(--input-small-label-font-size)); }
95
100
  }
96
101
 
97
102
  .SInputBase.medium {
98
- .label { padding-bottom: 8px; height: 24px; }
103
+ .label { padding-bottom: 6px; min-height: 26px; }
99
104
  .label-text { font-size: var(--input-label-font-size, var(--input-medium-label-font-size)); }
100
105
  }
101
106
 
@@ -107,9 +112,9 @@ function getErrorMsg(validation: Validatable) {
107
112
 
108
113
  .label {
109
114
  display: flex;
110
- align-items: baseline;
115
+ align-items: flex-start;
111
116
  width: 100%;
112
- line-height: 16px;
117
+ line-height: 20px;
113
118
  cursor: pointer;
114
119
  transition: color 0.25s;
115
120
  }
@@ -122,8 +127,18 @@ function getErrorMsg(validation: Validatable) {
122
127
 
123
128
  :deep(.STooltip) {
124
129
  .label-info {
125
- display: inline-block;
126
- margin-left: 4px;
130
+ display: flex;
131
+ justify-content: center;
132
+ align-items: center;
133
+ flex-shrink: 0;
134
+ margin-top: -2px;
135
+ margin-left: 2px;
136
+ margin-bottom: -2px;
137
+ width: 24px;
138
+ height: 24px;
139
+ }
140
+
141
+ .label-info-icon {
127
142
  width: 16px;
128
143
  height: 16px;
129
144
  color: var(--c-text-2);
@@ -131,7 +146,7 @@ function getErrorMsg(validation: Validatable) {
131
146
  }
132
147
 
133
148
  &:hover, &:focus, &:focus-within {
134
- .label-info {
149
+ .label-info-icon {
135
150
  color: var(--c-text-info-1);
136
151
  }
137
152
  }
@@ -143,10 +158,17 @@ function getErrorMsg(validation: Validatable) {
143
158
 
144
159
  .label-note {
145
160
  display: inline-block;
161
+ flex-shrink: 0;
146
162
  margin-left: 8px;
163
+ padding-top: 1px;
164
+ line-height: 19px;
147
165
  font-size: 12px;
148
166
  font-weight: 400;
149
167
  color: var(--c-text-2);
168
+
169
+ &.has-info {
170
+ margin-left: 4px;
171
+ }
150
172
  }
151
173
 
152
174
  .help {
@@ -168,7 +190,7 @@ function getErrorMsg(validation: Validatable) {
168
190
  .help-text {
169
191
  max-width: 65ch;
170
192
  margin: 0;
171
- padding: 4px 0 0;
193
+ padding: 6px 0 0;
172
194
  line-height: 20px;
173
195
  font-size: 12px;
174
196
  font-weight: 400;
@@ -183,10 +205,13 @@ function getErrorMsg(validation: Validatable) {
183
205
 
184
206
  .check {
185
207
  display: inline-flex;
208
+ flex-shrink: 0;
186
209
  align-items: center;
187
210
  gap: 4px;
188
211
  margin-left: auto;
189
- line-height: 16px;
212
+ padding-top: 1px;
213
+ padding-left: 8px;
214
+ line-height: 19px;
190
215
  font-size: 12px;
191
216
 
192
217
  &.neutral { color: var(--c-text-1); }
@@ -162,6 +162,14 @@ function emitBlur() {
162
162
  color: var(--input-placeholder-color);
163
163
  }
164
164
 
165
+ &:hover {
166
+ border-color: var(--input-hover-border-color);
167
+ }
168
+
169
+ &:focus {
170
+ border-color: var(--input-focus-border-color);
171
+ }
172
+
165
173
  &.disabled {
166
174
  border-color: var(--input-disabled-border-color);
167
175
  background-color: var(--input-disabled-bg-color);
@@ -195,7 +195,7 @@ function handleArray(value: OptionValue) {
195
195
  .box-content {
196
196
  padding: 3px 30px 3px 12px;
197
197
  line-height: 24px;
198
- font-size: 14px;
198
+ font-size: var(--input-font-size, var(--input-mini-font-size));
199
199
  }
200
200
 
201
201
  .box-icon {
@@ -212,7 +212,7 @@ function handleArray(value: OptionValue) {
212
212
  .box-content {
213
213
  padding: 5px 30px 5px 8px;
214
214
  line-height: 24px;
215
- font-size: 16px;
215
+ font-size: var(--input-font-size, var(--input-small-font-size));
216
216
  }
217
217
 
218
218
  .box-icon {
@@ -229,7 +229,7 @@ function handleArray(value: OptionValue) {
229
229
  .box-content {
230
230
  padding: 11px 44px 11px 16px;
231
231
  line-height: 24px;
232
- font-size: 16px;
232
+ font-size: var(--input-font-size, var(--input-medium-font-size));
233
233
  }
234
234
 
235
235
  .box-icon {
@@ -255,7 +255,7 @@ function handleArray(value: OptionValue) {
255
255
 
256
256
  .SInputDropdown.has-error {
257
257
  .box {
258
- border-color: var(--c-danger);
258
+ border-color: var(--input-error-border-color);
259
259
  }
260
260
  }
261
261
 
@@ -265,37 +265,22 @@ function handleArray(value: OptionValue) {
265
265
 
266
266
  .box {
267
267
  position: relative;
268
- border: 1px solid var(--c-divider);
268
+ border: 1px solid var(--input-border-color);
269
269
  border-radius: 6px;
270
270
  width: 100%;
271
271
  color: var(--input-text);
272
- background-color: var(--c-bg);
272
+ background-color: var(--input-bg-color);;
273
273
  cursor: pointer;
274
- transition: border-color .25s, background-color .25s;
274
+ transition: border-color 0.25s, background-color 0.25s;
275
275
 
276
276
  &:hover {
277
- border-color: var(--c-black);
278
- }
279
-
280
- &:focus,
281
- &:hover.focus {
282
- border-color: var(--c-info);
283
- }
284
-
285
- .dark &:hover {
286
- border-color: var(--c-gray);
287
- }
288
-
289
- .dark &:focus,
290
- .dark &:hover:focus {
291
- border-color: var(--c-info);
277
+ border-color: var(--input-hover-border-color);
292
278
  }
293
279
  }
294
280
 
295
281
  .box-placeholder {
296
282
  padding: 2px 4px;
297
- font-weight: 500;
298
- color: var(--c-text-3);
283
+ color: var(--input-placeholder-color);
299
284
  overflow: hidden;
300
285
  white-space: nowrap;
301
286
  }
@@ -130,7 +130,7 @@ function onChange(e: Event) {
130
130
  .button {
131
131
  padding: 0 8px;
132
132
  line-height: 26px;
133
- font-size: 13px;
133
+ font-size: 12px;
134
134
  font-weight: 500;
135
135
  }
136
136
 
@@ -150,7 +150,7 @@ function onChange(e: Event) {
150
150
  .button {
151
151
  padding: 0 12px;
152
152
  line-height: 30px;
153
- font-size: 14px;
153
+ font-size: 13px;
154
154
  font-weight: 500;
155
155
  }
156
156
 
@@ -184,6 +184,11 @@ function onChange(e: Event) {
184
184
  &:hover {
185
185
  border-color: var(--input-hover-border-color);
186
186
  }
187
+
188
+ &:hover .button {
189
+ border-color: var(--c-border-mute-2);
190
+ background-color: var(--c-bg-mute-2);
191
+ }
187
192
  }
188
193
 
189
194
  .action {
@@ -191,11 +196,11 @@ function onChange(e: Event) {
191
196
  }
192
197
 
193
198
  .button {
194
- border: 1px solid var(--c-divider);
195
- border-radius: 4px;
199
+ border: 1px solid var(--c-border-mute-1);
200
+ border-radius: 3px;
196
201
  color: var(--c-text-1);
197
- background-color: var(--c-mute);
198
- transition: background-color 0.25s;
202
+ background-color: var(--c-bg-mute-1);
203
+ transition: border-color 0.25s, background-color 0.25s;
199
204
  }
200
205
 
201
206
  .text {
@@ -307,7 +307,8 @@ function createRequiredTouched(): boolean[] {
307
307
  }
308
308
 
309
309
  .input {
310
- font-family: var(--input-value-font-family, var(--font-family-number));
310
+ font-family: var(--input-value-font-family, var(--font-family-base));
311
+ font-feature-settings: "tnum";
311
312
  line-height: 24px;
312
313
  background-color: transparent;
313
314
 
@@ -6,7 +6,8 @@ import SInputBase from './SInputBase.vue'
6
6
  import SInputSegmentsOption, { type Mode } from './SInputSegmentsOption.vue'
7
7
 
8
8
  export type Size = 'mini' | 'small' | 'medium'
9
- export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
9
+ export type Color = 'default' | 'mute' | 'neutral' | 'info' | 'success' | 'warning' | 'danger'
10
+ export type CheckColor = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
10
11
 
11
12
  export interface Option<T extends string | number | boolean> {
12
13
  label: string
@@ -24,7 +25,7 @@ const props = defineProps<{
24
25
  help?: string
25
26
  checkIcon?: IconifyIcon
26
27
  checkText?: string
27
- checkColor?: Color
28
+ checkColor?: CheckColor
28
29
  options: Option<T>[]
29
30
  block?: boolean
30
31
  disabled?: boolean
@@ -78,7 +79,7 @@ function onSelect(value: T) {
78
79
  :size="size ?? 'small'"
79
80
  :label="option.label"
80
81
  :value="option.value"
81
- :mode="option.mode ?? 'neutral'"
82
+ :mode="option.mode ?? 'default'"
82
83
  :active="_value === option.value"
83
84
  :disabled="disabled ? true : option.disabled ?? false"
84
85
  @click="onSelect(option.value)"
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts" generic="T extends string | number | boolean">
2
2
  export type Size = 'mini' | 'small' | 'medium'
3
- export type Mode = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
3
+ export type Mode = 'default' | 'mute' | 'neutral' | 'info' | 'success' | 'warning' | 'danger'
4
4
 
5
5
  const props = defineProps<{
6
6
  size: Size
@@ -67,7 +67,7 @@ function onClick() {
67
67
  width: 1px;
68
68
  height: 16px;
69
69
  border-radius: 4px;
70
- background-color: var(--c-divider-2);
70
+ background-color: var(--c-divider);
71
71
  content: "";
72
72
  transition: opacity 0.25s;
73
73
  }
@@ -108,18 +108,24 @@ function onClick() {
108
108
  }
109
109
  }
110
110
 
111
- .SInputSegmentsOption.neutral.active {
112
- border-color: var(--button-fill-mute-border-color);
113
- color: var(--button-fill-mute-text-color);
114
- background-color: var(--button-fill-mute-bg-color);
111
+ .SInputSegmentsOption.default.active {
112
+ border-color: var(--button-fill-default-border-color);
113
+ color: var(--button-fill-default-text-color);
114
+ background-color: var(--button-fill-default-bg-color);
115
115
  }
116
116
 
117
117
  .SInputSegmentsOption.mute.active {
118
118
  border-color: var(--button-fill-mute-border-color);
119
- color: var(--c-text-2);
119
+ color: var(--button-fill-mute-text-color);
120
120
  background-color: var(--button-fill-mute-bg-color);
121
121
  }
122
122
 
123
+ .SInputSegmentsOption.neutral.active {
124
+ border-color: var(--button-fill-neutral-border-color);
125
+ color: var(--button-fill-neutral-text-color);
126
+ background-color: var(--button-fill-neutral-bg-color);
127
+ }
128
+
123
129
  .SInputSegmentsOption.info.active {
124
130
  border-color: var(--button-fill-info-border-color);
125
131
  color: var(--button-fill-info-text-color);
@@ -304,7 +304,7 @@ function createRequiredTouched(): boolean[] {
304
304
 
305
305
  .input {
306
306
  line-height: 24px;
307
- font-family: var(--input-value-font-family, var(--font-family-number));
307
+ font-family: var(--input-value-font-family, var(--font-family-base));
308
308
  font-feature-settings: "tnum";
309
309
  background-color: transparent;
310
310
 
@@ -2,7 +2,8 @@
2
2
  import SM, { type Props } from './SM.vue'
3
3
 
4
4
  withDefaults(defineProps<Props>(), {
5
- opacity: 0
5
+ opacity: 0,
6
+ once: true
6
7
  })
7
8
  </script>
8
9
 
@@ -1,3 +1,4 @@
1
+ import { isClient } from '@vueuse/core'
1
2
  import { type MaybeRefOrGetter, type Ref, ref, toValue, watchEffect } from 'vue'
2
3
  import { isFile, isString } from '../support/Utils'
3
4
 
@@ -13,7 +14,13 @@ export interface ImageSrcFromFile {
13
14
  export function useImageSrcFromFile(
14
15
  file: MaybeRefOrGetter<File | string | null>
15
16
  ): ImageSrcFromFile {
16
- const reader = new FileReader()
17
+ if (!isClient) {
18
+ return {
19
+ src: ref(null)
20
+ }
21
+ }
22
+
23
+ const reader = new window.FileReader()
17
24
  const src = ref<string | null>(null)
18
25
 
19
26
  reader.onload = function () {
@@ -2,6 +2,7 @@ import { type App } from 'vue'
2
2
  import SDesc from '../components/SDesc.vue'
3
3
  import SDescDay from '../components/SDescDay.vue'
4
4
  import SDescEmpty from '../components/SDescEmpty.vue'
5
+ import SDescFile from '../components/SDescFile.vue'
5
6
  import SDescItem from '../components/SDescItem.vue'
6
7
  import SDescLabel from '../components/SDescLabel.vue'
7
8
  import SDescLink from '../components/SDescLink.vue'
@@ -14,6 +15,7 @@ export function mixin(app: App): void {
14
15
  app.component('SDesc', SDesc)
15
16
  app.component('SDescDay', SDescDay)
16
17
  app.component('SDescEmpty', SDescEmpty)
18
+ app.component('SDescFile', SDescFile)
17
19
  app.component('SDescItem', SDescItem)
18
20
  app.component('SDescLabel', SDescLabel)
19
21
  app.component('SDescLink', SDescLink)
@@ -28,6 +30,7 @@ declare module 'vue' {
28
30
  SDesc: typeof SDesc
29
31
  SDescDay: typeof SDescDay
30
32
  SDescEmpty: typeof SDescEmpty
33
+ SDescFile: typeof SDescFile
31
34
  SDescItem: typeof SDescItem
32
35
  SDescLabel: typeof SDescLabel
33
36
  SDescLink: typeof SDescLink
@@ -153,6 +153,7 @@
153
153
  /**
154
154
  * Color: Divider and Gutter
155
155
  * -------------------------------------------------------------------------- */
156
+
156
157
  :root {
157
158
  --c-divider: #e0e0e1;
158
159
  --c-gutter: #e2e2e3;
@@ -166,6 +167,7 @@
166
167
  /**
167
168
  * Color: Neutral
168
169
  * -------------------------------------------------------------------------- */
170
+
169
171
  :root {
170
172
  --c-neutral-1: var(--c-neutral-light-1);
171
173
  --c-neutral-2: var(--c-neutral-light-2);
@@ -1,8 +1,9 @@
1
- import { decimal as baseDecimal, helpers } from '@vuelidate/validators'
1
+ import { and, decimal as baseDecimal, helpers, not } from '@vuelidate/validators'
2
+ import { hyphen } from '../validators'
2
3
 
3
4
  export function decimal(msg?: string) {
4
5
  return helpers.withMessage(
5
6
  () => msg ?? 'The value must be valid decimal numbers.',
6
- baseDecimal
7
+ and(not(hyphen), baseDecimal)
7
8
  )
8
9
  }
@@ -0,0 +1,8 @@
1
+ import { decimal as baseDecimal, helpers } from '@vuelidate/validators'
2
+
3
+ export function decimalOrHyphen(msg?: string) {
4
+ return helpers.withMessage(
5
+ () => msg ?? 'The value must be valid decimal numbers or just a hyphen.',
6
+ baseDecimal
7
+ )
8
+ }
@@ -1,6 +1,7 @@
1
1
  export { and, not, or } from '@vuelidate/validators'
2
2
  export * from './checked'
3
3
  export * from './decimal'
4
+ export * from './decimalOrHyphen'
4
5
  export * from './email'
5
6
  export * from './fileExtension'
6
7
  export * from './hms'
@@ -11,6 +12,8 @@ export * from './maxValue'
11
12
  export * from './minLength'
12
13
  export * from './minValue'
13
14
  export * from './month'
15
+ export * from './negativeInteger'
16
+ export * from './positiveInteger'
14
17
  export * from './required'
15
18
  export * from './requiredHms'
16
19
  export * from './requiredIf'
@@ -18,3 +21,5 @@ export * from './requiredYmd'
18
21
  export * from './rule'
19
22
  export * from './url'
20
23
  export * from './ymd'
24
+ export * from './zeroOrNegativeInteger'
25
+ export * from './zeroOrPositiveInteger'
@@ -0,0 +1,9 @@
1
+ import { helpers } from '@vuelidate/validators'
2
+ import { negativeInteger as baseNegativeInteger } from '../validators'
3
+
4
+ export function negativeInteger(msg?: string) {
5
+ return helpers.withMessage(
6
+ () => msg ?? 'The value must be valid negative integer.',
7
+ baseNegativeInteger
8
+ )
9
+ }
@@ -0,0 +1,9 @@
1
+ import { helpers } from '@vuelidate/validators'
2
+ import { positiveInteger as basePositiveInteger } from '../validators'
3
+
4
+ export function positiveInteger(msg?: string) {
5
+ return helpers.withMessage(
6
+ () => msg ?? 'The value must be valid positive integer.',
7
+ basePositiveInteger
8
+ )
9
+ }
@@ -0,0 +1,9 @@
1
+ import { helpers, or } from '@vuelidate/validators'
2
+ import { negativeInteger, zero } from '../validators'
3
+
4
+ export function zeroOrNegativeInteger(msg?: string) {
5
+ return helpers.withMessage(
6
+ () => msg ?? 'The value must be zero or valid negative integer.',
7
+ or(zero, negativeInteger)
8
+ )
9
+ }
@@ -0,0 +1,9 @@
1
+ import { helpers, or } from '@vuelidate/validators'
2
+ import { positiveInteger, zero } from '../validators'
3
+
4
+ export function zeroOrPositiveInteger(msg?: string) {
5
+ return helpers.withMessage(
6
+ () => msg ?? 'The value must be zero or valid positive integer.',
7
+ or(zero, positiveInteger)
8
+ )
9
+ }
@@ -0,0 +1,3 @@
1
+ export function hyphen(value: string): boolean {
2
+ return value === '-'
3
+ }
@@ -1,9 +1,13 @@
1
1
  export * from './checked'
2
2
  export * from './fileExtension'
3
3
  export * from './hms'
4
+ export * from './hyphen'
4
5
  export * from './maxFileSize'
5
6
  export * from './maxTotalFileSize'
6
7
  export * from './month'
8
+ export * from './negativeInteger'
9
+ export * from './positiveInteger'
7
10
  export * from './requiredHms'
8
11
  export * from './requiredYmd'
9
12
  export * from './ymd'
13
+ export * from './zero'
@@ -0,0 +1,3 @@
1
+ export function negativeInteger(value: number): boolean {
2
+ return Number.isInteger(value) && value < 0
3
+ }
@@ -0,0 +1,3 @@
1
+ export function positiveInteger(value: number): boolean {
2
+ return Number.isInteger(value) && value > 0
3
+ }
@@ -0,0 +1,3 @@
1
+ export function zero(value: number): boolean {
2
+ return value === 0
3
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "3.4.0",
4
- "packageManager": "pnpm@8.10.4",
3
+ "version": "3.6.0",
4
+ "packageManager": "pnpm@8.10.5",
5
5
  "description": "Vue Components for Global Brain Design System.",
6
6
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",
7
7
  "license": "MIT",
@@ -31,6 +31,7 @@
31
31
  "lint": "eslint --fix .",
32
32
  "lint:fail": "eslint .",
33
33
  "vitest": "vitest",
34
+ "vitest:run": "vitest run",
34
35
  "coverage": "vitest run --coverage",
35
36
  "test": "pnpm run type && pnpm run lint && pnpm run coverage",
36
37
  "test:fail": "pnpm run type && pnpm run lint:fail && pnpm run coverage",
@@ -71,15 +72,15 @@
71
72
  "@types/body-scroll-lock": "^3.1.2",
72
73
  "@types/lodash-es": "^4.17.11",
73
74
  "@types/markdown-it": "^13.0.6",
74
- "@types/node": "^20.9.0",
75
- "@vitejs/plugin-vue": "^4.4.1",
76
- "@vitest/coverage-v8": "^1.0.0-beta.4",
77
- "@vue/test-utils": "^2.4.1",
75
+ "@types/node": "^20.9.2",
76
+ "@vitejs/plugin-vue": "^4.5.0",
77
+ "@vitest/coverage-v8": "^1.0.0-beta.5",
78
+ "@vue/test-utils": "^2.4.2",
78
79
  "@vuelidate/core": "^2.0.3",
79
80
  "@vuelidate/validators": "^2.0.4",
80
81
  "@vueuse/core": "^10.6.1",
81
82
  "body-scroll-lock": "4.0.0-beta.0",
82
- "eslint": "^8.53.0",
83
+ "eslint": "^8.54.0",
83
84
  "fuse.js": "^7.0.0",
84
85
  "happy-dom": "^12.10.3",
85
86
  "histoire": "^0.17.5",
@@ -89,12 +90,13 @@
89
90
  "pinia": "^2.1.7",
90
91
  "postcss": "^8.4.31",
91
92
  "postcss-nested": "^6.0.1",
93
+ "punycode": "^2.3.1",
92
94
  "release-it": "^17.0.0",
93
95
  "typescript": "~5.2.2",
94
96
  "v-calendar": "^3.1.2",
95
- "vite": "^4.5.0",
96
- "vitepress": "1.0.0-rc.25",
97
- "vitest": "^1.0.0-beta.4",
97
+ "vite": "^5.0.0",
98
+ "vitepress": "1.0.0-rc.29",
99
+ "vitest": "^1.0.0-beta.5",
98
100
  "vue": "^3.3.8",
99
101
  "vue-router": "^4.2.5",
100
102
  "vue-tsc": "^1.8.22"