@globalbrain/sefirot 0.69.0 → 0.72.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/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ # [0.72.0](https://github.com/globalbrain/sefirot/compare/v0.71.0...v0.72.0) (2021-12-01)
2
+
3
+ ### Features
4
+
5
+ * **icon:** add `copy` icon ([#109](https://github.com/globalbrain/sefirot/issues/109)) ([a5dfd72](https://github.com/globalbrain/sefirot/commit/a5dfd726691baecc454efb7b14c9b0028305577b))
6
+ * **input-select:** add `small` size and `nullable` option ([b416612](https://github.com/globalbrain/sefirot/commit/b416612e8c2881920499dc36deb7f7c2b74d9bd3))
7
+ * **input-checkbox:** add size option to input checkbox ([cfddd90](https://github.com/globalbrain/sefirot/commit/cfddd9003685a4b4984f69bafaead92bc84219e3))
8
+
9
+ # [0.71.0](https://github.com/globalbrain/sefirot/compare/v0.70.1...v0.71.0) (2021-11-18)
10
+
11
+ ### Features
12
+
13
+ * **icon:** add `send` icon ([683a57a](https://github.com/globalbrain/sefirot/commit/683a57ad712952b1de122c35a8113cf8f1a738ee))
14
+
15
+ ## [0.70.1](https://github.com/globalbrain/sefirot/compare/v0.70.0...v0.70.1) (2021-11-17)
16
+
17
+ ### Bug Fixes
18
+
19
+ * **input-text:** fix long text getting wrapped inside display value ([19f3ccd](https://github.com/globalbrain/sefirot/commit/19f3ccdf78600a28cd16f970319c46c44100fc26))
20
+
21
+ # [0.70.0](https://github.com/globalbrain/sefirot/compare/v0.69.0...v0.70.0) (2021-11-17)
22
+
23
+ ### Bug Fixes
24
+
25
+ * **link:** remove unexpected trailing white space ([9e5592c](https://github.com/globalbrain/sefirot/commit/9e5592c33e111834229fa2e7c95a4f44284088f8))
26
+
27
+ ### Features
28
+
29
+ * **input-text:** make small and outlined style as default and fix small styling issues ([2ba7691](https://github.com/globalbrain/sefirot/commit/2ba76916e9ab8a2df2002686695e63fea9bcdd35))
30
+ * **input-select:** add error-message option for select input ([#108](https://github.com/globalbrain/sefirot/issues/108)) ([b949136](https://github.com/globalbrain/sefirot/commit/b949136bd0df6cc041ace47a7e16c31e2985ffbe))
31
+ * **input-checkbox:** improve styling and add validation support ([1da8bf1](https://github.com/globalbrain/sefirot/commit/1da8bf163260ecde620e7e485f9b1b2bcda71a2c))
32
+ * **input-file:** add SInputFile component ([#107](https://github.com/globalbrain/sefirot/issues/107)) ([03eb6bb](https://github.com/globalbrain/sefirot/commit/03eb6bbcbba94e6c31693406c41efd402f23c4aa))
33
+ * **validation:** add "checked" rule ([a24c3ba](https://github.com/globalbrain/sefirot/commit/a24c3ba6186fa6ac2c6cf3b361301c5a6a8a62ee))
34
+
1
35
  # [0.69.0](https://github.com/globalbrain/sefirot/compare/v0.68.0...v0.69.0) (2021-10-28)
2
36
 
3
37
  ### Bug Fixes
@@ -38,7 +38,7 @@
38
38
  --c-divider-light-2: rgba(60, 60, 67, .12);
39
39
 
40
40
  --c-divider-dark-1: rgba(84, 84, 88, .65);
41
- --c-divider-dark-2: rgba(84, 84, 88, .65);
41
+ --c-divider-dark-2: rgba(84, 84, 84, .48);
42
42
 
43
43
  --c-text-light-1: var(--c-black);
44
44
  --c-text-light-2: rgba(60, 60, 67, .6);
@@ -300,6 +300,7 @@
300
300
 
301
301
  --input-outlined-bg-disabled: var(--c-white-mute);
302
302
  --input-outlined-border: var(--c-divider);
303
+ --input-outlined-border-focus: var(--c-black);
303
304
 
304
305
  --input-clear-bg-disabled: var(--c-white);
305
306
 
@@ -320,6 +321,7 @@
320
321
 
321
322
  .dark-mode {
322
323
  --input-outlined-bg-disabled: var(--c-black-mute);
324
+ --input-outlined-border-focus: var(--c-gray);
323
325
 
324
326
  --input-focus-border: var(--c-gray);
325
327
  }
@@ -1,10 +1,12 @@
1
1
  <template>
2
2
  <SInputBase
3
3
  class="SInputCheckbox"
4
+ :class="[size]"
4
5
  :name="name"
5
6
  :label="label"
6
7
  :note="note"
7
8
  :help="help"
9
+ :validation="validation"
8
10
  >
9
11
  <div class="container">
10
12
  <div class="input" :class="{ on: value }" role="button" @click="emitChange">
@@ -21,10 +23,13 @@
21
23
  </template>
22
24
 
23
25
  <script lang="ts">
24
- import { defineComponent } from '@vue/composition-api'
26
+ import { PropType, defineComponent } from '@vue/composition-api'
27
+ import { Validation } from '../validation/Validation'
25
28
  import SIconCheck from './icons/SIconCheck.vue'
26
29
  import SInputBase from './SInputBase.vue'
27
30
 
31
+ type Size = 'mini' | 'small' | 'medium'
32
+
28
33
  export default defineComponent({
29
34
  components: {
30
35
  SIconCheck,
@@ -37,12 +42,14 @@ export default defineComponent({
37
42
  },
38
43
 
39
44
  props: {
45
+ size: { type: String as PropType<Size>, default: 'small' },
40
46
  name: { type: String, default: null },
41
47
  label: { type: String, default: null },
42
48
  note: { type: String, default: null },
43
49
  help: { type: String, default: null },
44
50
  text: { type: String, required: true },
45
- value: { type: Boolean, required: true }
51
+ value: { type: Boolean, required: true },
52
+ validation: { type: Object as PropType<Validation>, default: null }
46
53
  },
47
54
 
48
55
  setup(props, { emit }) {
@@ -92,7 +99,7 @@ export default defineComponent({
92
99
 
93
100
  .box {
94
101
  position: relative;
95
- border: 2px solid var(--c-gray-dark-1);
102
+ border: 2px solid var(--c-text-3);
96
103
  border-radius: 2px;
97
104
  width: 18px;
98
105
  height: 18px;
@@ -122,5 +129,6 @@ export default defineComponent({
122
129
  padding-left: 12px;
123
130
  line-height: 20px;
124
131
  font-size: 14px;
132
+ font-weight: 500;
125
133
  }
126
134
  </style>
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <SInputBase
3
3
  class="SInputCheckboxes"
4
+ :class="[size]"
4
5
  :name="name"
5
6
  :label="label"
6
7
  :note="note"
@@ -21,10 +22,12 @@
21
22
  </template>
22
23
 
23
24
  <script lang="ts">
24
- import { defineComponent } from '@vue/composition-api'
25
+ import { PropType, defineComponent } from '@vue/composition-api'
25
26
  import SInputBase from './SInputBase.vue'
26
27
  import SInputCheckbox from './SInputCheckbox.vue'
27
28
 
29
+ type Size = 'mini' | 'small' | 'medium'
30
+
28
31
  export default defineComponent({
29
32
  components: {
30
33
  SInputBase,
@@ -37,6 +40,7 @@ export default defineComponent({
37
40
  },
38
41
 
39
42
  props: {
43
+ size: { type: String as PropType<Size>, default: 'small' },
40
44
  name: { type: String, default: null },
41
45
  label: { type: String, default: null },
42
46
  note: { type: String, default: null },
@@ -0,0 +1,227 @@
1
+ <template>
2
+ <SInputBase
3
+ class="SInputFile"
4
+ :class="classes"
5
+ :name="name"
6
+ :label="label"
7
+ :note="note"
8
+ :help="help"
9
+ :error-message="errorMessage"
10
+ :validation="validation"
11
+ >
12
+ <input
13
+ ref="input"
14
+ class="input"
15
+ type="file"
16
+ @change="e => handleChange(e.target.files[0])"
17
+ >
18
+
19
+ <div class="box" role="button" @click="open">
20
+ <div class="action">
21
+ <button class="button">
22
+ {{ text }}
23
+ </button>
24
+ </div>
25
+
26
+ <div class="text">
27
+ <p v-if="fileName" class="file-name">{{ fileName }}</p>
28
+ <p v-else-if="placeholder" class="placeholder">{{ placeholder }}</p>
29
+ </div>
30
+ </div>
31
+ </SInputBase>
32
+ </template>
33
+
34
+ <script lang="ts">
35
+ import { PropType, defineComponent, ref, computed } from '@vue/composition-api'
36
+ import { Validation } from '../validation/Validation'
37
+ import SInputBase from './SInputBase.vue'
38
+
39
+ type Size = 'mini' | 'small' | 'medium'
40
+
41
+ export default defineComponent({
42
+ components: {
43
+ SInputBase
44
+ },
45
+
46
+ props: {
47
+ size: { type: String as PropType<Size>, default: 'small' },
48
+ name: { type: String, default: null },
49
+ label: { type: String, default: null },
50
+ note: { type: String, default: null },
51
+ text: { type: String, default: 'Choose File' },
52
+ help: { type: String, default: null },
53
+ placeholder: { type: String, default: null },
54
+ errorMessage: { type: Boolean, default: true },
55
+ value: { type: File, default: null },
56
+ validation: { type: Object as PropType<Validation>, default: null }
57
+ },
58
+
59
+ setup(props, { emit }) {
60
+ const input = ref<HTMLInputElement | null>(null)
61
+
62
+ const classes = computed(() => [props.size])
63
+
64
+ const fileName = computed(() => {
65
+ return props.value ? props.value.name : null
66
+ })
67
+
68
+ function open(): void {
69
+ input.value!.click()
70
+ }
71
+
72
+ function handleChange(file?: File): void {
73
+ emit('input', file ?? null)
74
+ file && props.validation?.$touch()
75
+ }
76
+
77
+ return {
78
+ input,
79
+ classes,
80
+ fileName,
81
+ open,
82
+ handleChange
83
+ }
84
+ }
85
+ })
86
+ </script>
87
+
88
+ <style lang="postcss" scoped>
89
+ @import "@/assets/styles/variables";
90
+
91
+ .SInputFile.mini {
92
+ .action {
93
+ padding: 2px 8px 2px 2px;
94
+ }
95
+
96
+ .button {
97
+ padding: 0 8px;
98
+ line-height: 22px;
99
+ font-size: 12px;
100
+ font-weight: 500;
101
+ }
102
+
103
+ .file-name,
104
+ .placeholder {
105
+ line-height: 30px;
106
+ font-size: 12px;
107
+ font-weight: 500;
108
+ }
109
+ }
110
+
111
+ .SInputFile.small {
112
+ .action {
113
+ padding: 5px 8px 5px 5px;
114
+ }
115
+
116
+ .button {
117
+ padding: 0 8px;
118
+ line-height: 26px;
119
+ font-size: 14px;
120
+ font-weight: 500;
121
+ }
122
+
123
+ .file-name,
124
+ .placeholder {
125
+ line-height: 38px;
126
+ font-size: 14px;
127
+ }
128
+
129
+ .placeholder {
130
+ font-weight: 500;
131
+ }
132
+ }
133
+
134
+ .SInputFile.medium {
135
+ .action {
136
+ padding: 7px 8px 7px 7px;
137
+ }
138
+
139
+ .button {
140
+ padding: 0 12px;
141
+ line-height: 30px;
142
+ font-size: 14px;
143
+ font-weight: 500;
144
+ }
145
+
146
+ .file-name,
147
+ .placeholder {
148
+ line-height: 46px;
149
+ font-size: 14px;
150
+ }
151
+
152
+ .placeholder {
153
+ font-weight: 500;
154
+ }
155
+ }
156
+
157
+ .SInputFile.has-error {
158
+ .box,
159
+ .box:hover {
160
+ border-color: var(--c-danger);
161
+ }
162
+ }
163
+
164
+ .input {
165
+ display: none;
166
+ }
167
+
168
+ .box {
169
+ display: flex;
170
+ border: 1px solid var(--input-outlined-border);
171
+ border-radius: 4px;
172
+ cursor: pointer;
173
+ transition: border-color .25s;
174
+
175
+ &:hover {
176
+ border-color: var(--input-outlined-border-focus);
177
+
178
+ .button {
179
+ background-color: var(--c-gray-light-4);
180
+ }
181
+ }
182
+ }
183
+
184
+ .dark-mode .box {
185
+ &:hover {
186
+ .button {
187
+ background-color: var(--c-gray-dark-3);
188
+ }
189
+ }
190
+ }
191
+
192
+ .action {
193
+ flex-shrink: 0;
194
+ }
195
+
196
+ .button {
197
+ border: 1px solid var(--c-divider-light);
198
+ border-radius: 4px;
199
+ color: var(--c-text-1);
200
+ background-color: var(--c-white-mute);
201
+ transition: background-color .25s;
202
+ }
203
+
204
+ .dark-mode .button {
205
+ background-color: var(--c-black-mute);
206
+ transition: background-color .25s;
207
+ }
208
+
209
+ .text {
210
+ flex-grow: 1;
211
+ overflow: hidden;
212
+ }
213
+
214
+ .file-name,
215
+ .placeholder {
216
+ margin: 0;
217
+ padding-right: 12px;
218
+ width: 100%;
219
+ white-space: nowrap;
220
+ overflow: hidden;
221
+ text-overflow: ellipsis;
222
+ }
223
+
224
+ .placeholder {
225
+ color: var(--input-placeholder);
226
+ }
227
+ </style>
@@ -34,7 +34,7 @@
34
34
  </template>
35
35
 
36
36
  <script lang="ts">
37
- import { defineComponent, computed, PropType } from '@vue/composition-api'
37
+ import { PropType, defineComponent, computed } from '@vue/composition-api'
38
38
  import { isNullish } from '../support/Util'
39
39
  import { Validation } from '../validation/Validation'
40
40
  import SInputText, { Size, Mode, Align, Color, Action } from './SInputText.vue'
@@ -19,9 +19,11 @@
19
19
  </template>
20
20
 
21
21
  <script lang="ts">
22
- import { defineComponent } from '@vue/composition-api'
22
+ import { PropType, defineComponent } from '@vue/composition-api'
23
23
  import SInputBase from './SInputBase.vue'
24
24
 
25
+ type Size = 'mini' | 'small' | 'medium'
26
+
25
27
  export default defineComponent({
26
28
  components: {
27
29
  SInputBase
@@ -33,6 +35,7 @@ export default defineComponent({
33
35
  },
34
36
 
35
37
  props: {
38
+ size: { type: String as PropType<Size>, default: 'small' },
36
39
  name: { type: String, default: null },
37
40
  label: { type: String, default: null },
38
41
  note: { type: String, default: null },
@@ -26,7 +26,7 @@ import { PropType, defineComponent } from '@vue/composition-api'
26
26
  import SInputBase from './SInputBase.vue'
27
27
  import SInputRadio from './SInputRadio.vue'
28
28
 
29
- export type Size = 'mini' | 'small' | 'medium'
29
+ type Size = 'mini' | 'small' | 'medium'
30
30
 
31
31
  export default defineComponent({
32
32
  components: {
@@ -40,7 +40,7 @@ export default defineComponent({
40
40
  },
41
41
 
42
42
  props: {
43
- size: { type: String as PropType<Size>, default: 'medium' },
43
+ size: { type: String as PropType<Size>, default: 'small' },
44
44
  name: { type: String, default: null },
45
45
  label: { type: String, default: null },
46
46
  note: { type: String, default: null },
@@ -7,6 +7,7 @@
7
7
  :note="note"
8
8
  :help="help"
9
9
  :validation="validation"
10
+ :error-message="errorMessage"
10
11
  >
11
12
  <div class="box" :class="{ focus: isFocused }">
12
13
  <select
@@ -19,12 +20,12 @@
19
20
  @change="emitChange"
20
21
  >
21
22
  <option
22
- v-if="placeholder"
23
- value=""
23
+ v-if="placeholder || nullable"
24
+ :value="JSON.stringify({ value: null })"
24
25
  :selected="isNotSelected"
25
- disabled
26
+ :disabled="!nullable"
26
27
  >
27
- {{ placeholder }}
28
+ {{ placeholder || 'Please select' }}
28
29
  </option>
29
30
 
30
31
  <option
@@ -53,8 +54,8 @@ import SIconChevronUp from './icons/SIconChevronUp.vue'
53
54
  import SIconChevronDown from './icons/SIconChevronDown.vue'
54
55
  import SInputBase from './SInputBase.vue'
55
56
 
56
- type Size = 'medium' | 'mini'
57
- type Mode = 'filled' | 'outlined'
57
+ type Size = 'mini' | 'small' | 'medium'
58
+ type Mode = 'outlined' | 'filled'
58
59
 
59
60
  interface Option {
60
61
  label: string
@@ -75,29 +76,29 @@ export default defineComponent({
75
76
  },
76
77
 
77
78
  props: {
78
- size: { type: String as PropType<Size>, default: 'medium' },
79
- mode: { type: String as PropType<Mode>, default: 'filled' },
79
+ size: { type: String as PropType<Size>, default: 'small' },
80
+ mode: { type: String as PropType<Mode>, default: 'outlined' },
80
81
  name: { type: String, default: null },
81
82
  label: { type: String, default: null },
82
83
  note: { type: String, default: null },
83
84
  help: { type: String, default: null },
84
85
  placeholder: { type: String, default: null },
85
- disabled: { type: Boolean, default: false },
86
86
  options: { type: Array as PropType<Option[]>, required: true },
87
- validation: { type: Object, default: null },
88
- value: { type: [String, Number, Boolean], default: null }
87
+ nullable: { type: Boolean, default: false },
88
+ disabled: { type: Boolean, default: false },
89
+ errorMessage: { type: Boolean, default: true },
90
+ value: { type: [String, Number, Boolean], default: null },
91
+ validation: { type: Object, default: null }
89
92
  },
90
93
 
91
94
  setup(props, { emit }) {
92
95
  const isFocused = ref(false)
93
96
 
94
- const classes = computed(() => ({
95
- medium: props.size === 'medium',
96
- mini: props.size === 'mini',
97
- filled: props.mode === 'filled',
98
- outlined: props.mode === 'outlined',
99
- disabled: props.disabled
100
- }))
97
+ const classes = computed(() => [
98
+ props.size,
99
+ props.mode,
100
+ { disabled: props.disabled }
101
+ ])
101
102
 
102
103
  const isNotSelected = computed(() => {
103
104
  return props.value === undefined || props.value === null || props.value === ''
@@ -116,7 +117,7 @@ export default defineComponent({
116
117
  }
117
118
 
118
119
  function emitChange(e: SyntheticInputEvent): void {
119
- props.validation && props.validation.$touch()
120
+ props.validation?.$touch()
120
121
 
121
122
  const option = JSON.parse(e.target.value)
122
123
 
@@ -156,6 +157,23 @@ export default defineComponent({
156
157
  }
157
158
  }
158
159
 
160
+ .SInputSelect.small {
161
+ .box {
162
+ height: 40px;
163
+ }
164
+
165
+ .select {
166
+ padding: 7px 30px 5px 12px;
167
+ line-height: 24px;
168
+ font-size: 16px;
169
+ }
170
+
171
+ .icon {
172
+ top: 7px;
173
+ right: 10px;
174
+ }
175
+ }
176
+
159
177
  .SInputSelect.medium {
160
178
  .box {
161
179
  height: 48px;
@@ -194,6 +212,12 @@ export default defineComponent({
194
212
  }
195
213
 
196
214
  .SInputSelect.outlined {
215
+ &.has-error {
216
+ .box {
217
+ border-color: var(--c-danger);
218
+ }
219
+ }
220
+
197
221
  .box {
198
222
  border-color: var(--input-outlined-border);
199
223
 
@@ -96,7 +96,7 @@ import SIconX from './icons/SIconX.vue'
96
96
  import SInputBase from './SInputBase.vue'
97
97
 
98
98
  export type Size = 'mini' | 'small' | 'medium'
99
- export type Mode = 'filled' | 'outlined'
99
+ export type Mode = 'outlined' | 'filled'
100
100
  export type Align = 'left' | 'center' | 'right'
101
101
  export type Color = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
102
102
  export type ColorCallback = (value: string | number) => Color
@@ -117,8 +117,8 @@ export default defineComponent({
117
117
  },
118
118
 
119
119
  props: {
120
- size: { type: String as PropType<Size>, default: 'medium' },
121
- mode: { type: String as PropType<Mode>, default: 'filled' },
120
+ size: { type: String as PropType<Size>, default: 'small' },
121
+ mode: { type: String as PropType<Mode>, default: 'outlined' },
122
122
  name: { type: String, default: null },
123
123
  label: { type: String, default: null },
124
124
  note: { type: String, default: null },
@@ -322,8 +322,8 @@ export default defineComponent({
322
322
  }
323
323
 
324
324
  .clear-svg {
325
- width: 10px;
326
- height: 10px;
325
+ width: 16px;
326
+ height: 16px;
327
327
  }
328
328
  }
329
329
 
@@ -357,7 +357,7 @@ export default defineComponent({
357
357
  font-size: 16px;
358
358
 
359
359
  &.has-icon {
360
- padding-left: 30px;
360
+ padding-left: 36px;
361
361
  }
362
362
 
363
363
  &.is-clearable {
@@ -366,13 +366,13 @@ export default defineComponent({
366
366
  }
367
367
 
368
368
  .icon {
369
- top: 9px;
370
- left: 10px;
369
+ top: 12px;
370
+ left: 12px;
371
371
  }
372
372
 
373
373
  .icon-svg {
374
- width: 14px;
375
- height: 14px;
374
+ width: 15px;
375
+ height: 15px;
376
376
  }
377
377
 
378
378
  .text {
@@ -390,13 +390,13 @@ export default defineComponent({
390
390
  .clear {
391
391
  top: 0;
392
392
  right: 0;
393
- width: 32px;
394
- height: 32px;
393
+ width: 40px;
394
+ height: 40px;
395
395
  }
396
396
 
397
397
  .clear-svg {
398
- width: 10px;
399
- height: 10px;
398
+ width: 16px;
399
+ height: 16px;
400
400
  }
401
401
  }
402
402
 
@@ -466,8 +466,8 @@ export default defineComponent({
466
466
  }
467
467
 
468
468
  .clear-svg {
469
- width: 10px;
470
- height: 10px;
469
+ width: 16px;
470
+ height: 16px;
471
471
  }
472
472
  }
473
473
 
@@ -617,6 +617,7 @@ export default defineComponent({
617
617
  .value {
618
618
  display: block;
619
619
  line-height: 24px;
620
+ white-space: nowrap;
620
621
  overflow: hidden;
621
622
  }
622
623
  }
@@ -8,8 +8,7 @@
8
8
  :target="target"
9
9
  :rel="rel"
10
10
  >
11
- <slot />
12
- <SIconExternalLink v-if="showExternal" class="SLink-icon" />
11
+ <slot /><SIconExternalLink v-if="showExternal" class="SLink-icon" />
13
12
  </component>
14
13
  </template>
15
14
 
@@ -11,7 +11,7 @@
11
11
  margin: 0;
12
12
  padding: 14px 16px 10px;
13
13
  line-height: 20px;
14
- font-size: 16px;
14
+ font-size: 14px;
15
15
  font-weight: 500;
16
16
  }
17
17
  </style>
@@ -8,17 +8,7 @@
8
8
  @import "@/assets/styles/variables";
9
9
 
10
10
  .SSheetMedium {
11
- padding: 24px 16px;
12
-
13
- @media (min-width: 768px) {
14
- padding: 24px;
15
- }
16
- }
17
-
18
- .SSheet.small .SSheetMedium {
19
- @media (min-width: 768px) {
20
- padding: 24px 16px;
21
- }
11
+ padding: 16px;
22
12
  }
23
13
 
24
14
  .SSheetMedium >>> h1 {
@@ -0,0 +1,6 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
3
+ <path d="M20,23H11a2.9,2.9,0,0,1-3-3V11a2.9,2.9,0,0,1,3-3h9a2.9,2.9,0,0,1,3,3v9A2.9,2.9,0,0,1,20,23ZM11,10a.9.9,0,0,0-1,1v9a.9.9,0,0,0,1,1h9a.9.9,0,0,0,1-1V11a.9.9,0,0,0-1-1Z" />
4
+ <path d="M5,16H4a2.9,2.9,0,0,1-3-3V4A2.9,2.9,0,0,1,4,1h9a2.9,2.9,0,0,1,3,3V5a1,1,0,0,1-2,0V4a.9.9,0,0,0-1-1H4A.9.9,0,0,0,3,4v9a.9.9,0,0,0,1,1H5a1,1,0,0,1,0,2Z" />
5
+ </svg>
6
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
3
+ <path d="M23,1.9c0-0.1,0-0.2-0.1-0.3c0,0,0-0.1,0-0.1c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.1-0.3-0.2c0,0-0.1,0-0.1,0c-0.1,0-0.2,0-0.3-0.1C22,1,22,1,22,1c-0.1,0-0.2,0-0.3,0.1l-20,7C1.3,8.2,1,8.5,1,9c0,0.4,0.2,0.8,0.6,1l8.6,3.8l3.8,8.6c0.2,0.4,0.5,0.6,0.9,0.6c0,0,0,0,0,0c0.4,0,0.8-0.3,0.9-0.7l7-20C23,2.2,23,2.1,23,1.9C23,2,23,2,23,1.9z M18.2,4.4l-7.4,7.4L4.7,9.1L18.2,4.4z M14.9,19.3l-2.7-6.1l7.4-7.4L14.9,19.3z" />
4
+ </svg>
5
+ </template>
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Get extension of the given file.
3
+ */
4
+ export function getExtension(file: File): string {
5
+ const name = file.name
6
+
7
+ return name.slice((name.lastIndexOf('.') - 1 >>> 0) + 2)
8
+ }
@@ -0,0 +1,11 @@
1
+ import { checked as baseChecked } from '../validators'
2
+ import { Rule } from './'
3
+
4
+ export default function checked(message?: string): Rule {
5
+ return {
6
+ name: 'checked',
7
+ message: message ?? 'You must check the field.',
8
+ optional: true,
9
+ validate: baseChecked
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { fileExtension as baseFileExtension } from '../validators'
2
+ import { Rule } from './'
3
+
4
+ export default function hms(extensions: string[], message?: string): Rule {
5
+ return {
6
+ name: 'fileExtension',
7
+ message: message ?? 'The file extension is invalid.',
8
+ optional: true,
9
+ validate: v => baseFileExtension(v, extensions)
10
+ }
11
+ }
@@ -1,7 +1,9 @@
1
1
  import { isString } from '../../support/Util'
2
+ import checked from './checked'
2
3
  import day from './day'
3
4
  import every from './every'
4
5
  import email from './email'
6
+ import fileExtension from './fileExtension'
5
7
  import hms from './hms'
6
8
  import include from './include'
7
9
  import includeSome from './includeSome'
@@ -41,9 +43,11 @@ export function locate(data: Record<string, any>, locator: Locator): any {
41
43
  }
42
44
 
43
45
  export {
46
+ checked,
44
47
  day,
45
48
  every,
46
49
  email,
50
+ fileExtension,
47
51
  hms,
48
52
  include,
49
53
  includeSome,
@@ -0,0 +1,3 @@
1
+ export default function checked(value: boolean): boolean {
2
+ return value === true
3
+ }
@@ -0,0 +1,15 @@
1
+ import { getExtension } from '../../support/File'
2
+
3
+ export default function fileExtension(file: File, extensions: string[]): boolean {
4
+ const fileExtension = getExtension(file)
5
+
6
+ return extensions.some((extension) => {
7
+ // If the extention option is `jpg`, we'll consider other variants such as
8
+ // `JPG`, `jpeg`, or `JPEG` to be valid as well for the sake of simplicity.
9
+ if (extension === 'jpg') {
10
+ return ['jpg', 'jpeg', 'JPG', 'JPEG'].includes(fileExtension)
11
+ }
12
+
13
+ return fileExtension === extension
14
+ })
15
+ }
@@ -1,5 +1,7 @@
1
+ import checked from './checked'
1
2
  import day from './day'
2
3
  import email from './email'
4
+ import fileExtension from './fileExtension'
3
5
  import hms from './hms'
4
6
  import include from './include'
5
7
  import includeSome from './includeSome'
@@ -22,8 +24,10 @@ import yearMonthDate from './yearMonthDate'
22
24
  import yearMonth from './yearMonth'
23
25
 
24
26
  export {
27
+ checked,
25
28
  day,
26
29
  email,
30
+ fileExtension,
27
31
  hms,
28
32
  include,
29
33
  includeSome,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "0.69.0",
3
+ "version": "0.72.0",
4
4
  "description": "Vue Components for Global Brain Design System.",
5
5
  "files": [
6
6
  "lib"