@globalbrain/sefirot 2.0.0-draft.2 → 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 +26 -0
- package/lib/components/SInputHMS.vue +1 -1
- package/lib/components/SInputNumber.vue +1 -1
- package/lib/components/SInputSelect.vue +235 -0
- package/lib/components/SInputText.vue +21 -3
- package/lib/components/SModal.vue +15 -37
- package/lib/components/SPortalModals.vue +2 -3
- package/lib/components/SSheet.vue +71 -13
- package/lib/components/SSheetFooter.vue +5 -1
- package/lib/components/SSheetFooterAction.vue +1 -1
- package/lib/components/SSheetFooterActions.vue +1 -1
- package/lib/components/SSheetMedium.vue +4 -10
- package/lib/components/SSheetTitle.vue +20 -0
- package/lib/components/icons/SIconMail.vue +6 -0
- package/lib/composables/Dropdown.ts +1 -1
- package/lib/composables/Modal.ts +3 -11
- package/lib/mixins/Sheet.ts +2 -4
- package/lib/store/modal/index.ts +0 -5
- package/lib/validation/rules/checked.ts +6 -4
- package/lib/validation/rules/email.ts +8 -0
- package/lib/validation/rules/fileExtension.ts +2 -2
- package/lib/validation/rules/hms.ts +2 -2
- package/lib/validation/rules/index.ts +2 -0
- package/lib/validation/rules/maxLength.ts +2 -4
- package/lib/validation/rules/minLength.ts +2 -4
- package/lib/validation/rules/required.ts +6 -1
- package/lib/validation/rules/requiredHms.ts +2 -2
- package/lib/validation/rules/requiredIf.ts +8 -2
- package/lib/validation/rules/requiredYmd.ts +2 -2
- package/lib/validation/rules/url.ts +8 -0
- package/lib/validation/rules/ymd.ts +2 -2
- package/package.json +30 -30
- package/lib/.DS_Store +0 -0
- package/lib/components/.DS_Store +0 -0
- package/lib/components/SSheetHeader.vue +0 -60
- package/lib/components/SSheetHeaderTitle.vue +0 -15
- package/lib/components/icons/.DS_Store +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
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
|
+
|
|
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)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **icon:** add a mail icon ([#115](https://github.com/globalbrain/sefirot/issues/115)) ([b3c24a7](https://github.com/globalbrain/sefirot/commit/b3c24a7602936fcba3fdc5ea51a8600adb15ec2c))
|
|
14
|
+
|
|
15
|
+
# [2.0.0-draft.4](https://github.com/globalbrain/sefirot/compare/v2.0.0-draft.3...v2.0.0-draft.4) (2021-12-22)
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* refactor modals ([36d0d1e](https://github.com/globalbrain/sefirot/commit/36d0d1e7747935aff4d909d4023751002875d6c8))
|
|
20
|
+
|
|
21
|
+
# [2.0.0-draft.3](https://github.com/globalbrain/sefirot/compare/v2.0.0-draft.2...v2.0.0-draft.3) (2021-12-17)
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* **input-text:** emit "enter" and "blur" event ([a7b2b38](https://github.com/globalbrain/sefirot/commit/a7b2b38dc5f4880e7b680601be6e607d9b180c68))
|
|
26
|
+
|
|
1
27
|
# [2.0.0-draft.2](https://github.com/globalbrain/sefirot/compare/v2.0.0-draft.1...v2.0.0-draft.2) (2021-12-14)
|
|
2
28
|
|
|
3
29
|
### Bug Fixes
|
|
@@ -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>
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
:value="modelValue"
|
|
17
17
|
@input="emitInput"
|
|
18
18
|
@blur="emitBlur"
|
|
19
|
+
@keypress.enter="emitEnter"
|
|
19
20
|
>
|
|
20
21
|
</SInputBase>
|
|
21
22
|
</template>
|
|
@@ -40,7 +41,11 @@ const props = defineProps({
|
|
|
40
41
|
validation: { type: Object as PropType<Validatable>, default: null }
|
|
41
42
|
})
|
|
42
43
|
|
|
43
|
-
const emit = defineEmits
|
|
44
|
+
const emit = defineEmits<{
|
|
45
|
+
(e: 'update:modelValue', value: string | null): void
|
|
46
|
+
(e: 'blur', value: string | null): void
|
|
47
|
+
(e: 'enter', value: string | null): void
|
|
48
|
+
}>()
|
|
44
49
|
|
|
45
50
|
const classes = computed(() => [
|
|
46
51
|
props.size,
|
|
@@ -52,11 +57,24 @@ function emitInput(e: Event): void {
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
function emitBlur(e: FocusEvent): void {
|
|
60
|
+
const value = getValue(e)
|
|
61
|
+
|
|
55
62
|
props.validation?.$touch()
|
|
56
|
-
|
|
63
|
+
|
|
64
|
+
emit('update:modelValue', value)
|
|
65
|
+
emit('blur', value)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function emitEnter(e: KeyboardEvent): void {
|
|
69
|
+
const value = getValue(e)
|
|
70
|
+
|
|
71
|
+
props.validation?.$touch()
|
|
72
|
+
|
|
73
|
+
emit('update:modelValue', value)
|
|
74
|
+
emit('enter', value)
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
function getValue(e: Event | FocusEvent): string | null {
|
|
77
|
+
function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
|
|
60
78
|
const value = (e.target as HTMLInputElement).value
|
|
61
79
|
|
|
62
80
|
return value === '' ? null : value
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-show="show" class="SModal" ref="el"
|
|
3
|
-
<div class="content"
|
|
4
|
-
<component :is="component" v-bind="data" @close="close" />
|
|
2
|
+
<div v-show="show" class="SModal" ref="el">
|
|
3
|
+
<div class="content">
|
|
4
|
+
<component :is="component" v-bind="data ?? {}" @close="close" />
|
|
5
5
|
</div>
|
|
6
6
|
</div>
|
|
7
7
|
</template>
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
11
11
|
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'
|
|
12
12
|
|
|
13
|
-
const props = defineProps
|
|
14
|
-
id
|
|
15
|
-
component:
|
|
16
|
-
data
|
|
17
|
-
show:
|
|
18
|
-
|
|
19
|
-
})
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
id?: number
|
|
15
|
+
component: any
|
|
16
|
+
data?: Record<string, any>
|
|
17
|
+
show: boolean
|
|
18
|
+
}>()
|
|
20
19
|
|
|
21
|
-
const emit = defineEmits
|
|
20
|
+
const emit = defineEmits<{
|
|
21
|
+
(e: 'close', id?: number): void
|
|
22
|
+
}>()
|
|
22
23
|
|
|
23
24
|
const el = ref<any>(null)
|
|
24
25
|
|
|
@@ -29,34 +30,16 @@ function close() {
|
|
|
29
30
|
emit('close', props.id)
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function
|
|
33
|
-
if (props.closable) {
|
|
34
|
-
if (!isDescendant(e.target)) {
|
|
35
|
-
close()
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function isDescendant(el: any): boolean {
|
|
41
|
-
if (el.classList && el.classList.contains('content')) {
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const parent = document.getElementsByClassName('content')[0]
|
|
46
|
-
|
|
47
|
-
return parent && parent.contains(el)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function lock(): void {
|
|
33
|
+
function lock() {
|
|
51
34
|
disableBodyScroll(el.value!, { reserveScrollBarGap: true })
|
|
52
35
|
}
|
|
53
36
|
|
|
54
|
-
function release()
|
|
37
|
+
function release() {
|
|
55
38
|
enableBodyScroll(el.value!)
|
|
56
39
|
}
|
|
57
40
|
</script>
|
|
58
41
|
|
|
59
|
-
<style lang="postcss"
|
|
42
|
+
<style scoped lang="postcss">
|
|
60
43
|
.SModal {
|
|
61
44
|
position: absolute;
|
|
62
45
|
top: 0;
|
|
@@ -64,13 +47,8 @@ function release(): void {
|
|
|
64
47
|
bottom: 0;
|
|
65
48
|
left: 0;
|
|
66
49
|
height: 100vh;
|
|
67
|
-
padding: 32px 16px 96px;
|
|
68
50
|
overflow: hidden;
|
|
69
51
|
overflow-y: auto;
|
|
70
|
-
|
|
71
|
-
@media (min-width: 768px) {
|
|
72
|
-
padding: 48px 24px 96px;
|
|
73
|
-
}
|
|
74
52
|
}
|
|
75
53
|
|
|
76
54
|
.SModal.fade-enter-active .content,
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
:show="index === items.length - 1"
|
|
10
10
|
:component="item.component"
|
|
11
11
|
:data="item.data"
|
|
12
|
-
:closable="item.options && item.options.closable"
|
|
13
12
|
@close="id => close(id)"
|
|
14
13
|
/>
|
|
15
14
|
</transition-group>
|
|
@@ -43,7 +42,7 @@ watch(hasItem, (value) => {
|
|
|
43
42
|
|
|
44
43
|
watch(route, closeAll)
|
|
45
44
|
|
|
46
|
-
function close(id
|
|
45
|
+
function close(id?: number) {
|
|
47
46
|
store.dispatch('modal/close', id)
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -52,7 +51,7 @@ function closeAll() {
|
|
|
52
51
|
}
|
|
53
52
|
</script>
|
|
54
53
|
|
|
55
|
-
<style lang="postcss"
|
|
54
|
+
<style scoped lang="postcss">
|
|
56
55
|
.SPortalModals {
|
|
57
56
|
position: fixed;
|
|
58
57
|
top: 0;
|
|
@@ -1,34 +1,92 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
2
|
+
<div class="SSheet" :class="[size ?? 'medium']" @click="closeIfClosable">
|
|
3
|
+
<article class="box" @click.stop>
|
|
4
|
+
<div v-if="closable !== false" class="close">
|
|
5
|
+
<button class="close-button" @click="$emit('close')">
|
|
6
|
+
<SIconX class="close-icon" />
|
|
7
|
+
</button>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<slot :close="() => emit('close')" />
|
|
11
|
+
</article>
|
|
12
|
+
</div>
|
|
5
13
|
</template>
|
|
6
14
|
|
|
7
15
|
<script setup lang="ts">
|
|
8
|
-
import
|
|
16
|
+
import SIconX from './icons/SIconX.vue'
|
|
9
17
|
|
|
10
|
-
defineProps<{
|
|
18
|
+
const props = defineProps<{
|
|
11
19
|
size?: 'small' | 'medium' | 'large'
|
|
20
|
+
closable?: boolean
|
|
12
21
|
}>()
|
|
13
22
|
|
|
14
|
-
const
|
|
23
|
+
const emit = defineEmits<{
|
|
24
|
+
(e: 'close'): void
|
|
25
|
+
}>()
|
|
15
26
|
|
|
16
|
-
function
|
|
17
|
-
|
|
27
|
+
function closeIfClosable() {
|
|
28
|
+
if (props.closable !== false) {
|
|
29
|
+
emit('close')
|
|
30
|
+
}
|
|
18
31
|
}
|
|
19
32
|
</script>
|
|
20
33
|
|
|
21
|
-
<style lang="postcss"
|
|
34
|
+
<style scoped lang="postcss">
|
|
22
35
|
.SSheet {
|
|
36
|
+
padding: 16px 16px 96px;
|
|
37
|
+
min-height: 100vh;
|
|
38
|
+
|
|
39
|
+
@media (min-width: 512px) {
|
|
40
|
+
padding: 32px 24px 96px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media (min-width: 768px) {
|
|
44
|
+
padding: 48px 32px 128px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.SSheet.small .box { max-width: 392px; }
|
|
49
|
+
.SSheet.medium .box { max-width: 512px; }
|
|
50
|
+
.SSheet.large .box { max-width: 768px; }
|
|
51
|
+
|
|
52
|
+
.box {
|
|
53
|
+
position: relative;
|
|
23
54
|
margin: 0 auto;
|
|
24
55
|
border: 1px solid var(--c-divider-light);
|
|
25
56
|
border-radius: 8px;
|
|
26
57
|
background-color: var(--c-bg);
|
|
27
58
|
box-shadow: var(--shadow-depth-3);
|
|
28
|
-
overflow: hidden;
|
|
29
59
|
}
|
|
30
60
|
|
|
31
|
-
.
|
|
32
|
-
|
|
33
|
-
|
|
61
|
+
.close {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 10px;
|
|
64
|
+
right: 4px;
|
|
65
|
+
z-index: 10;
|
|
66
|
+
|
|
67
|
+
@media (min-width: 512px) {
|
|
68
|
+
top: 14px;
|
|
69
|
+
right: 12px;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.close-button {
|
|
74
|
+
display: flex;
|
|
75
|
+
justify-content: center;
|
|
76
|
+
align-items: center;
|
|
77
|
+
width: 48px;
|
|
78
|
+
height: 48px;
|
|
79
|
+
color: var(--c-text-3);
|
|
80
|
+
transition: color .25s;
|
|
81
|
+
|
|
82
|
+
&:hover {
|
|
83
|
+
color: var(--c-text-1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.close-icon {
|
|
88
|
+
width: 20px;
|
|
89
|
+
height: 20px;
|
|
90
|
+
fill: currentColor;
|
|
91
|
+
}
|
|
34
92
|
</style>
|
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
</footer>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
|
-
<style lang="postcss"
|
|
7
|
+
<style scoped lang="postcss">
|
|
8
8
|
.SSheetFooter {
|
|
9
9
|
border-top: 1px solid var(--c-divider-light);
|
|
10
10
|
padding: 0 16px;
|
|
11
|
+
|
|
12
|
+
@media (min-width: 512px) {
|
|
13
|
+
padding: 0 24px;
|
|
14
|
+
}
|
|
11
15
|
}
|
|
12
16
|
</style>
|
|
@@ -6,17 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
<style lang="postcss" scoped>
|
|
8
8
|
.SSheetMedium {
|
|
9
|
-
padding: 16px;
|
|
10
|
-
}
|
|
9
|
+
padding: 12px 16px 20px;
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
font-weight: 500;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.SSheetMedium :deep(h1 + p) {
|
|
19
|
-
margin-top: 8px;
|
|
11
|
+
@media (min-width: 512px) {
|
|
12
|
+
padding: 16px 24px 26px;
|
|
13
|
+
}
|
|
20
14
|
}
|
|
21
15
|
|
|
22
16
|
.SSheetMedium :deep(p) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header class="SSheetTitle">
|
|
3
|
+
<h1 class="title">
|
|
4
|
+
<slot />
|
|
5
|
+
</h1>
|
|
6
|
+
</header>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<style lang="postcss" scoped>
|
|
10
|
+
.title {
|
|
11
|
+
padding: 20px 48px 0 16px;
|
|
12
|
+
line-height: 28px;
|
|
13
|
+
font-size: 20px;
|
|
14
|
+
font-weight: 500;
|
|
15
|
+
|
|
16
|
+
@media (min-width: 512px) {
|
|
17
|
+
padding: 24px 64px 0 24px;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
3
|
+
<path d="M20,21H4a3,3,0,0,1-3-3V6A3,3,0,0,1,4,3H20a3,3,0,0,1,3,3V18A3,3,0,0,1,20,21ZM4,5A1,1,0,0,0,3,6V18a1,1,0,0,0,1,1H20a1,1,0,0,0,1-1V6a1,1,0,0,0-1-1Z" />
|
|
4
|
+
<path d="M12,14a1,1,0,0,1-.57-.18l-10-7A1,1,0,1,1,2.57,5.18L12,11.78l9.43-6.6a1,1,0,1,1,1.14,1.64l-10,7A1,1,0,0,1,12,14Z" />
|
|
5
|
+
</svg>
|
|
6
|
+
</template>
|
package/lib/composables/Modal.ts
CHANGED
|
@@ -1,29 +1,21 @@
|
|
|
1
1
|
import { useStore } from 'vuex'
|
|
2
|
-
import { Options as ModalOptions } from '../store/modal'
|
|
3
2
|
|
|
4
3
|
export interface Modal {
|
|
5
|
-
open(
|
|
4
|
+
open(data?: Record<string, any>): Promise<any>
|
|
6
5
|
close(): Promise<any>
|
|
7
6
|
}
|
|
8
7
|
|
|
9
|
-
export interface OpenOptions {
|
|
10
|
-
id?: number
|
|
11
|
-
component?: any
|
|
12
|
-
data?: Record<string, any>
|
|
13
|
-
options?: ModalOptions
|
|
14
|
-
}
|
|
15
|
-
|
|
16
8
|
let modalId = 0
|
|
17
9
|
|
|
18
10
|
export function useModal(component: any): Modal {
|
|
19
11
|
const store = useStore()
|
|
20
12
|
const id = useModalId()
|
|
21
13
|
|
|
22
|
-
function open(
|
|
14
|
+
function open(data: Record<string, any> = {}): Promise<any> {
|
|
23
15
|
return store.dispatch('modal/open', {
|
|
24
16
|
id,
|
|
25
17
|
component,
|
|
26
|
-
|
|
18
|
+
data
|
|
27
19
|
})
|
|
28
20
|
}
|
|
29
21
|
|
package/lib/mixins/Sheet.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { App } from 'vue'
|
|
2
2
|
import SSheet from '../components/SSheet.vue'
|
|
3
|
-
import
|
|
4
|
-
import SSheetHeaderTitle from '../components/SSheetHeaderTitle.vue'
|
|
3
|
+
import SSheetTitle from '../components/SSheetTitle.vue'
|
|
5
4
|
import SSheetMedium from '../components/SSheetMedium.vue'
|
|
6
5
|
import SSheetFooter from '../components/SSheetFooter.vue'
|
|
7
6
|
import SSheetFooterActions from '../components/SSheetFooterActions.vue'
|
|
@@ -11,8 +10,7 @@ export function mixin(app: App): void {
|
|
|
11
10
|
app.mixin({
|
|
12
11
|
components: {
|
|
13
12
|
SSheet,
|
|
14
|
-
|
|
15
|
-
SSheetHeaderTitle,
|
|
13
|
+
SSheetTitle,
|
|
16
14
|
SSheetMedium,
|
|
17
15
|
SSheetFooter,
|
|
18
16
|
SSheetFooterActions,
|
package/lib/store/modal/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { helpers } from '@vuelidate/validators'
|
|
2
2
|
import { checked as baseChecked } from '../validators/checked'
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
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
|
+
}
|
|
@@ -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[],
|
|
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
|
|
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(
|
|
4
|
-
|
|
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
|
}
|
|
@@ -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.
|
|
3
|
+
"version": "2.0.0-draft.6",
|
|
4
4
|
"description": "Vue Components for Global Brain Design System.",
|
|
5
5
|
"files": [
|
|
6
6
|
"lib"
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"type": "vue-tsc --noEmit",
|
|
10
10
|
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix './{lib,tests}/**/*.{ts,vue}'",
|
|
11
11
|
"lint:fail": "eslint --ext .js,.vue --ignore-path .gitignore './{lib,tests}/**/*.{ts,vue}'",
|
|
12
|
-
"
|
|
13
|
-
"coverage": "
|
|
12
|
+
"vitest": "vitest",
|
|
13
|
+
"coverage": "vitest run --coverage",
|
|
14
14
|
"test": "yarn type && yarn lint && yarn coverage",
|
|
15
15
|
"test:fail": "yarn type && yarn lint:fail && yarn coverage",
|
|
16
16
|
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
|
@@ -33,50 +33,50 @@
|
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@types/body-scroll-lock": "^3.1.0",
|
|
35
35
|
"@types/lodash-es": "^4.17.5",
|
|
36
|
-
"@vuelidate/core": "^2.0.0-alpha.
|
|
37
|
-
"@vuelidate/validators": "^2.0.0-alpha.
|
|
36
|
+
"@vuelidate/core": "^2.0.0-alpha.34",
|
|
37
|
+
"@vuelidate/validators": "^2.0.0-alpha.26",
|
|
38
38
|
"body-scroll-lock": "^4.0.0-beta.0",
|
|
39
|
-
"fuse.js": "^6.
|
|
39
|
+
"fuse.js": "^6.5.3",
|
|
40
40
|
"lodash-es": "^4.17.21",
|
|
41
41
|
"normalize.css": "^8.0.1",
|
|
42
|
-
"postcss": "^8.4.
|
|
42
|
+
"postcss": "^8.4.6",
|
|
43
43
|
"postcss-nested": "^5.0.6",
|
|
44
|
-
"typescript": "^4.
|
|
45
|
-
"vue": "^3.2.
|
|
44
|
+
"typescript": "^4.5.5",
|
|
45
|
+
"vue": "^3.2.31",
|
|
46
46
|
"vue-router": "^4.0.12",
|
|
47
|
-
"vue-tsc": "^0.
|
|
47
|
+
"vue-tsc": "^0.31.4",
|
|
48
48
|
"vuex": "^4.0.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@babel/core": "^7.
|
|
51
|
+
"@babel/core": "^7.17.4",
|
|
52
52
|
"@types/body-scroll-lock": "^3.1.0",
|
|
53
|
-
"@types/
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
53
|
+
"@types/lodash-es": "^4.17.6",
|
|
54
|
+
"@typescript-eslint/parser": "^5.12.0",
|
|
55
|
+
"@vitejs/plugin-vue": "^2.2.0",
|
|
56
56
|
"@vue/test-utils": "^2.0.0-rc.17",
|
|
57
|
-
"@vuelidate/core": "^2.0.0-alpha.
|
|
58
|
-
"@vuelidate/validators": "^2.0.0-alpha.
|
|
59
|
-
"babel-jest": "^26.0.0",
|
|
57
|
+
"@vuelidate/core": "^2.0.0-alpha.34",
|
|
58
|
+
"@vuelidate/validators": "^2.0.0-alpha.26",
|
|
60
59
|
"body-scroll-lock": "^4.0.0-beta.0",
|
|
60
|
+
"c8": "^7.11.0",
|
|
61
61
|
"codecov": "^3.8.3",
|
|
62
|
-
"conventional-changelog-cli": "^2.
|
|
62
|
+
"conventional-changelog-cli": "^2.2.2",
|
|
63
63
|
"dayjs": "^1.10.7",
|
|
64
|
-
"
|
|
65
|
-
"eslint
|
|
66
|
-
"eslint-plugin-vue": "^8.
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
64
|
+
"enquirer": "^2.3.6",
|
|
65
|
+
"eslint": "^8.9.0",
|
|
66
|
+
"eslint-plugin-vue": "^8.4.1",
|
|
67
|
+
"execa": "^5.1.1",
|
|
68
|
+
"fuse.js": "^6.5.3",
|
|
69
|
+
"happy-dom": "^2.31.1",
|
|
70
70
|
"lodash-es": "^4.17.21",
|
|
71
71
|
"normalize.css": "^8.0.1",
|
|
72
|
-
"postcss": "^8.4.
|
|
72
|
+
"postcss": "^8.4.6",
|
|
73
73
|
"postcss-nested": "^5.0.6",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"vue
|
|
74
|
+
"typescript": "^4.5.5",
|
|
75
|
+
"vite": "^2.8.3",
|
|
76
|
+
"vitest": "^0.3.5",
|
|
77
|
+
"vue": "^3.2.31",
|
|
78
78
|
"vue-router": "^4.0.12",
|
|
79
|
-
"vue-tsc": "^0.
|
|
79
|
+
"vue-tsc": "^0.31.4",
|
|
80
80
|
"vuex": "^4.0.2"
|
|
81
81
|
}
|
|
82
82
|
}
|
package/lib/.DS_Store
DELETED
|
Binary file
|
package/lib/components/.DS_Store
DELETED
|
Binary file
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<header class="SSheetHeader">
|
|
3
|
-
<slot />
|
|
4
|
-
|
|
5
|
-
<div v-if="closable" class="close">
|
|
6
|
-
<button class="close-button" @click="close">
|
|
7
|
-
<SIconX class="close-icon" />
|
|
8
|
-
</button>
|
|
9
|
-
</div>
|
|
10
|
-
</header>
|
|
11
|
-
</template>
|
|
12
|
-
|
|
13
|
-
<script setup lang="ts">
|
|
14
|
-
import { useStore } from 'vuex'
|
|
15
|
-
import SIconX from './icons/SIconX.vue'
|
|
16
|
-
|
|
17
|
-
defineProps({
|
|
18
|
-
closable: { type: Boolean, default: true }
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
const store = useStore()
|
|
22
|
-
|
|
23
|
-
function close() {
|
|
24
|
-
store.dispatch('modal/close')
|
|
25
|
-
}
|
|
26
|
-
</script>
|
|
27
|
-
|
|
28
|
-
<style lang="postcss" scoped>
|
|
29
|
-
.SSheetHeader {
|
|
30
|
-
display: flex;
|
|
31
|
-
justify-content: space-between;
|
|
32
|
-
border-bottom: 1px solid var(--c-divider-light);
|
|
33
|
-
min-height: 48px;
|
|
34
|
-
background-color: var(--c-bg-soft);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.close {
|
|
38
|
-
margin: 0 0 0 auto;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.close-button {
|
|
42
|
-
display: flex;
|
|
43
|
-
justify-content: center;
|
|
44
|
-
align-items: center;
|
|
45
|
-
width: 48px;
|
|
46
|
-
height: 47px;
|
|
47
|
-
color: var(--c-text-3);
|
|
48
|
-
transition: color .25s;
|
|
49
|
-
|
|
50
|
-
&:hover {
|
|
51
|
-
color: var(--c-text-1);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.close-icon {
|
|
56
|
-
width: 20px;
|
|
57
|
-
height: 20px;
|
|
58
|
-
fill: currentColor;
|
|
59
|
-
}
|
|
60
|
-
</style>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<h1 class="SSheetHeaderTitle">
|
|
3
|
-
<slot />
|
|
4
|
-
</h1>
|
|
5
|
-
</template>
|
|
6
|
-
|
|
7
|
-
<style lang="postcss" scoped>
|
|
8
|
-
.SSheetHeaderTitle {
|
|
9
|
-
margin: 0;
|
|
10
|
-
padding: 14px 16px 10px;
|
|
11
|
-
line-height: 20px;
|
|
12
|
-
font-size: 14px;
|
|
13
|
-
font-weight: 500;
|
|
14
|
-
}
|
|
15
|
-
</style>
|
|
Binary file
|