@globalbrain/sefirot 2.0.0-draft.5 → 2.0.0-draft.6

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,11 @@
1
+ # [2.0.0-draft.6](https://github.com/globalbrain/sefirot/compare/v2.0.0-draft.5...v2.0.0-draft.6) (2022-02-21)
2
+
3
+ ### Features
4
+
5
+ * **input-select:** add SInputSelect component ([1d0608e](https://github.com/globalbrain/sefirot/commit/1d0608e8aaa3f7007bb4aff18255ce2b528a7d76))
6
+ * **validation:** add custom error message support ([acb0894](https://github.com/globalbrain/sefirot/commit/acb0894ec5fdcc052100e3623525fbf030854a2f))
7
+
8
+
1
9
  # [2.0.0-draft.5](https://github.com/globalbrain/sefirot/compare/v2.0.0-draft.4...v2.0.0-draft.5) (2022-02-16)
2
10
 
3
11
  ### Features
@@ -0,0 +1,235 @@
1
+ <template>
2
+ <SInputBase
3
+ class="SInputSelect"
4
+ :class="classes"
5
+ :label="label"
6
+ :note="note"
7
+ :help="help"
8
+ :error-message="errorMessage ?? true"
9
+ :validation="validation"
10
+ >
11
+ <div class="box" :class="{ focus: isFocused }">
12
+ <select
13
+ class="select"
14
+ :class="{ 'is-not-selected': isNotSelected }"
15
+ :disabled="disabled"
16
+ @focus="focus"
17
+ @blur="blur"
18
+ @change="emitChange"
19
+ >
20
+ <option
21
+ v-if="placeholder || nullable"
22
+ :value="JSON.stringify({ value: null })"
23
+ :selected="isNotSelected"
24
+ :disabled="!nullable"
25
+ >
26
+ {{ placeholder || 'Please select' }}
27
+ </option>
28
+
29
+ <option
30
+ v-for="(option, index) in options"
31
+ :key="index"
32
+ :style="{ display: option.disabled ? 'none' : undefined }"
33
+ :value="JSON.stringify(option)"
34
+ :selected="isSelectedOption(option)"
35
+ >
36
+ {{ option.label }}
37
+ </option>
38
+ </select>
39
+
40
+ <div class="icon" role="button">
41
+ <SIconChevronUp class="icon-svg up" />
42
+ <SIconChevronDown class="icon-svg down" />
43
+ </div>
44
+ </div>
45
+ </SInputBase>
46
+ </template>
47
+
48
+ <script setup lang="ts">
49
+ import { PropType, ref, computed } from 'vue'
50
+ import { Validation, Validatable } from '../composables/Validation'
51
+ import SIconChevronUp from './icons/SIconChevronUp.vue'
52
+ import SIconChevronDown from './icons/SIconChevronDown.vue'
53
+ import SInputBase from './SInputBase.vue'
54
+
55
+ type Size = 'mini' | 'small' | 'medium'
56
+
57
+ interface Option {
58
+ label: string
59
+ value: boolean | number | string
60
+ disabled?: boolean
61
+ }
62
+
63
+ const props = defineProps({
64
+ size: { type: String as PropType<Size>, default: 'small' },
65
+ label: { type: String, default: null },
66
+ note: { type: String, default: null },
67
+ help: { type: String, default: null },
68
+ placeholder: { type: String, default: null },
69
+ options: { type: Array as PropType<Option[]>, required: true },
70
+ disabled: { type: Boolean, default: false },
71
+ nullable: { type: Boolean, default: false },
72
+ errorMessage: { type: Boolean, default: true },
73
+ modelValue: { type: [String, Number, Boolean] as PropType<string | number | boolean | null>, default: null },
74
+ validation: { type: Object as PropType<Validatable>, default: null }
75
+ })
76
+
77
+ const emit = defineEmits(['update:modelValue'])
78
+
79
+ const isFocused = ref(false)
80
+
81
+ const classes = computed(() => [
82
+ props.size ?? 'small',
83
+ { disabled: props.disabled ?? false }
84
+ ])
85
+
86
+ const isNotSelected = computed(() => {
87
+ return props.modelValue === undefined || props.modelValue === null || props.modelValue === ''
88
+ })
89
+
90
+ function isSelectedOption(option: Option): boolean {
91
+ return option.value === props.modelValue
92
+ }
93
+
94
+ function focus() {
95
+ isFocused.value = true
96
+ }
97
+
98
+ function blur() {
99
+ isFocused.value = false
100
+ }
101
+
102
+ function emitChange(e: any): void {
103
+ props.validation?.$touch()
104
+
105
+ const option = JSON.parse(e.target.value)
106
+
107
+ emit('update:modelValue', option.value)
108
+ }
109
+ </script>
110
+
111
+ <style scoped lang="postcss">
112
+ .SInputSelect.mini {
113
+ .box {
114
+ height: 32px;
115
+ }
116
+
117
+ .select {
118
+ padding: 3px 30px 3px 12px;
119
+ line-height: 24px;
120
+ font-size: 14px;
121
+ }
122
+
123
+ .icon {
124
+ top: 3px;
125
+ right: 8px;
126
+ }
127
+ }
128
+
129
+ .SInputSelect.small {
130
+ .box {
131
+ height: 40px;
132
+ }
133
+
134
+ .select {
135
+ padding: 7px 30px 5px 12px;
136
+ line-height: 24px;
137
+ font-size: 16px;
138
+ }
139
+
140
+ .icon {
141
+ top: 7px;
142
+ right: 10px;
143
+ }
144
+ }
145
+
146
+ .SInputSelect.medium {
147
+ .box {
148
+ height: 48px;
149
+ }
150
+
151
+ .select {
152
+ padding: 11px 44px 11px 16px;
153
+ line-height: 24px;
154
+ font-size: 16px;
155
+ }
156
+
157
+ .icon {
158
+ top: 11px;
159
+ right: 12px;
160
+ }
161
+ }
162
+
163
+ .SInputSelect.disabled {
164
+ .box {
165
+ background-color: var(--c-bg-mute);
166
+ }
167
+
168
+ .box:hover .select {
169
+ cursor: not-allowed;
170
+ }
171
+ }
172
+
173
+ .SInputSelect.has-error {
174
+ .box {
175
+ border-color: var(--c-danger);
176
+ }
177
+
178
+ .select {
179
+ border-color: var(--c-danger);
180
+ }
181
+ }
182
+
183
+ .box {
184
+ position: relative;
185
+ border: 1px solid var(--input-border);
186
+ border-radius: 4px;
187
+ width: 100%;
188
+ color: var(--input-text);
189
+ cursor: pointer;
190
+ transition: border-color .25s, background-color .25s;
191
+
192
+ &:hover,
193
+ &.focus {
194
+ border-color: var(--input-border);
195
+ }
196
+
197
+ &:focus:not(:focus-visible) {
198
+ border-color: var(--input-border);
199
+ outline: 0;
200
+ }
201
+ }
202
+
203
+ .select {
204
+ position: relative;
205
+ z-index: 20;
206
+ display: block;
207
+ border: 0;
208
+ border-radius: 4px;
209
+ width: 100%;
210
+ background-color: transparent;
211
+ cursor: pointer;
212
+
213
+ &.select.is-not-selected {
214
+ color: var(--input-placeholder);
215
+ font-weight: 500;
216
+ }
217
+ }
218
+
219
+ .icon {
220
+ position: absolute;
221
+ z-index: 10;
222
+ cursor: pointer;
223
+ }
224
+
225
+ .icon-svg {
226
+ display: block;
227
+ width: 14px;
228
+ height: 14px;
229
+ fill: var(--input-placeholder);
230
+ }
231
+
232
+ .icon-svg.up {
233
+ margin-bottom: -4px;
234
+ }
235
+ </style>
@@ -1,7 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
2
  import { checked as baseChecked } from '../validators/checked'
3
3
 
4
- export const checked = helpers.withMessage(
5
- 'You must check the field.',
6
- (value: boolean) => !helpers.req(value) || baseChecked(value)
7
- )
4
+ export function checked(msg?: string) {
5
+ return helpers.withMessage(
6
+ () => msg ?? 'You must check the field.',
7
+ (value: boolean) => !helpers.req(value) || baseChecked(value)
8
+ )
9
+ }
@@ -0,0 +1,8 @@
1
+ import { helpers, email as baseEmail } from '@vuelidate/validators'
2
+
3
+ export function email(msg?: string) {
4
+ return helpers.withMessage(
5
+ () => msg ?? 'The Email is invalid.',
6
+ baseEmail
7
+ )
8
+ }
@@ -1,9 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
2
  import { fileExtension as baseFileExtension } from '../validators/fileExtension'
3
3
 
4
- export function fileExtension(extensions: string[], message?: string) {
4
+ export function fileExtension(extensions: string[], msg?: string) {
5
5
  return helpers.withMessage(
6
- 'The file extension is invalid.',
6
+ () => msg ?? 'The file extension is invalid.',
7
7
  (value: File) => {
8
8
  return !helpers.req(value) || baseFileExtension(value, extensions)
9
9
  }
@@ -1,9 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
2
  import { hms as baseHms, Hms, HmsType } from '../validators/hms'
3
3
 
4
- export function hms(required?: HmsType[]) {
4
+ export function hms(required?: HmsType[], msg?: string) {
5
5
  return helpers.withMessage(
6
- 'The time is invalid.',
6
+ () => msg ?? 'The time is invalid.',
7
7
  (value: Hms) => {
8
8
  return !helpers.req(value) || baseHms(value, required)
9
9
  }
@@ -1,4 +1,5 @@
1
1
  export * from './checked'
2
+ export * from './email'
2
3
  export * from './fileExtension'
3
4
  export * from './hms'
4
5
  export * from './maxLength'
@@ -7,4 +8,5 @@ export * from './required'
7
8
  export * from './requiredHms'
8
9
  export * from './requiredIf'
9
10
  export * from './requiredYmd'
11
+ export * from './url'
10
12
  export * from './ymd'
@@ -1,11 +1,9 @@
1
1
  import { helpers, maxLength as baseMaxLength } from '@vuelidate/validators'
2
2
 
3
- export function maxLength(length: number) {
3
+ export function maxLength(length: number, msg?: string) {
4
4
  return helpers.withMessage(
5
5
  ({ $params }) => {
6
- return `
7
- The value must be less or equal to ${($params as any).max} characters.
8
- `
6
+ return msg ?? `The value must be less or equal to ${($params as any).max} characters.`
9
7
  },
10
8
  baseMaxLength(length)
11
9
  )
@@ -1,11 +1,9 @@
1
1
  import { helpers, minLength as baseMinLength } from '@vuelidate/validators'
2
2
 
3
- export function minLength(length: number) {
3
+ export function minLength(length: number, msg?: string) {
4
4
  return helpers.withMessage(
5
5
  ({ $params }) => {
6
- return `
7
- The value must be greater or equal to ${($params as any).min} characters.
8
- `
6
+ return msg ?? `The value must be greater or equal to ${($params as any).min} characters.`
9
7
  },
10
8
  baseMinLength(length)
11
9
  )
@@ -1,3 +1,8 @@
1
1
  import { helpers, required as baseRequired } from '@vuelidate/validators'
2
2
 
3
- export const required = helpers.withMessage('The field is required.', baseRequired)
3
+ export function required(msg?: string) {
4
+ return helpers.withMessage(
5
+ () => msg ?? 'The field is required.',
6
+ baseRequired
7
+ )
8
+ }
@@ -1,9 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
2
  import { requiredHms as baseRequiredHms, Hms, HmsType } from '../validators/requiredHms'
3
3
 
4
- export function requiredHms(required?: HmsType[]) {
4
+ export function requiredHms(required?: HmsType[], msg?: string) {
5
5
  return helpers.withMessage(
6
- 'The field is required.',
6
+ () => msg ?? 'The field is required.',
7
7
  (value: Hms) => {
8
8
  return !helpers.req(value) || baseRequiredHms(value, required)
9
9
  }
@@ -1,5 +1,11 @@
1
1
  import { helpers, requiredIf as baseRequiredIf } from '@vuelidate/validators'
2
2
 
3
- export function requiredIf(prop: boolean | string | (() => boolean | Promise<boolean>)) {
4
- return helpers.withMessage('The field is required.', baseRequiredIf(prop))
3
+ export function requiredIf(
4
+ prop: boolean | string | (() => boolean | Promise<boolean>),
5
+ msg?: string
6
+ ) {
7
+ return helpers.withMessage(
8
+ () => msg ?? 'The field is required.',
9
+ baseRequiredIf(prop)
10
+ )
5
11
  }
@@ -1,9 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
2
  import { requiredYmd as baseRequiredYmd, Ymd, YmdType } from '../validators/requiredYmd'
3
3
 
4
- export function requiredYmd(required?: YmdType[]) {
4
+ export function requiredYmd(required?: YmdType[], msg?: string) {
5
5
  return helpers.withMessage(
6
- 'The field is required.',
6
+ () => msg ?? 'The field is required.',
7
7
  (value: Ymd) => {
8
8
  return !helpers.req(value) || baseRequiredYmd(value, required)
9
9
  }
@@ -0,0 +1,8 @@
1
+ import { helpers, url as baseUrl } from '@vuelidate/validators'
2
+
3
+ export function url(msg?: string) {
4
+ return helpers.withMessage(
5
+ () => msg ?? 'The URL is invalid.',
6
+ baseUrl
7
+ )
8
+ }
@@ -1,9 +1,9 @@
1
1
  import { helpers } from '@vuelidate/validators'
2
2
  import { ymd as baseYmd, Ymd, YmdType } from '../validators/ymd'
3
3
 
4
- export function ymd(required?: YmdType[]) {
4
+ export function ymd(required?: YmdType[], msg?: string) {
5
5
  return helpers.withMessage(
6
- 'The date is invalid.',
6
+ () => msg ?? 'The date is invalid.',
7
7
  (value: Ymd) => {
8
8
  return !helpers.req(value) || baseYmd(value, required)
9
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "2.0.0-draft.5",
3
+ "version": "2.0.0-draft.6",
4
4
  "description": "Vue Components for Global Brain Design System.",
5
5
  "files": [
6
6
  "lib"