@globalbrain/sefirot 2.13.0 → 2.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Global Brain Corporation
3
+ Copyright (c) 2023 Global Brain Corporation
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,50 +1,75 @@
1
1
  <script setup lang="ts">
2
- import { computed, unref } from 'vue'
2
+ import IconQuestion from '@iconify-icons/ph/question'
3
+ import { computed, unref, useSlots } from 'vue'
3
4
  import { Validatable } from '../composables/Validation'
5
+ import SIcon from './SIcon.vue'
6
+ import STooltip from './STooltip.vue'
4
7
 
5
8
  const props = defineProps<{
6
9
  name?: string
7
- note?: string
8
10
  label?: string
11
+ info?: string
12
+ note?: string
9
13
  help?: string
10
14
  hideError?: boolean
11
15
  validation?: Validatable
12
16
  }>()
13
17
 
14
- const hasError = computed(() => {
15
- if (!props.validation) {
16
- return false
17
- }
18
+ const slots = useSlots()
18
19
 
19
- return props.validation.$dirty && props.validation.$invalid
20
+ const hasInfo = computed(() => {
21
+ return slots.info || props.info
20
22
  })
21
23
 
22
- const errorMsg = computed(() => {
24
+ const error = computed(() => {
23
25
  if (!props.validation) {
24
26
  return null
25
27
  }
26
28
 
27
- const errors = props.validation.$errors
29
+ const errorMsg = getErrorMsg(props.validation)
30
+ const hasError = isDirtyAndInvalid(props.validation)
31
+ const showError = !props.hideError && hasError && errorMsg
28
32
 
29
- return errors.length > 0 ? unref(errors[0].$message) : null
33
+ return {
34
+ has: hasError,
35
+ show: showError,
36
+ msg: errorMsg
37
+ }
30
38
  })
31
39
 
32
- const showError = computed(() => {
33
- return !props.hideError && hasError.value && errorMsg.value
34
- })
40
+ function isDirtyAndInvalid(validation: Validatable) {
41
+ return validation.$dirty && validation.$invalid
42
+ }
43
+
44
+ function getErrorMsg(validation: Validatable) {
45
+ const errors = validation.$errors
46
+
47
+ return errors.length > 0 ? unref(errors[0].$message) : null
48
+ }
35
49
  </script>
36
50
 
37
51
  <template>
38
- <div class="SInputBase" :class="{ 'has-error': hasError }">
52
+ <div class="SInputBase" :class="{ 'has-error': error?.has }">
39
53
  <label v-if="label" class="label" :for="name">
40
- {{ label }} <span class="note">{{ note }}</span>
54
+ <STooltip v-if="hasInfo" :text="info">
55
+ <div class="label-text">
56
+ <span class="label-text-value">{{ label }}</span>
57
+ <SIcon class="label-text-info" :icon="IconQuestion" />
58
+ </div>
59
+ <template v-if="$slots.info" #text><slot name="info" /></template>
60
+ </STooltip>
61
+ <div v-else class="label-text">
62
+ <span class="label-text-value">{{ label }}</span>
63
+ </div>
64
+
65
+ <span class="label-note">{{ note }}</span>
41
66
  </label>
42
67
 
43
68
  <slot />
44
69
 
45
70
  <div class="help">
46
71
  <slot name="before-help" />
47
- <p v-if="showError" class="help-error">{{ errorMsg }}</p>
72
+ <p v-if="error?.show" class="help-error">{{ error.msg }}</p>
48
73
  <p v-if="help" class="help-text">{{ help }}</p>
49
74
  </div>
50
75
  </div>
@@ -52,43 +77,61 @@ const showError = computed(() => {
52
77
 
53
78
  <style lang="postcss" scoped>
54
79
  .SInputBase.mini {
55
- .label {
56
- padding-bottom: 6px;
57
- font-size: 12px;
58
- }
80
+ .label { padding-bottom: 6px; }
81
+ .label-text-value { font-size: 12px; }
82
+ .label-text-info { width: 14px; height: 14px; }
59
83
  }
60
84
 
61
85
  .SInputBase.small {
62
- .label {
63
- padding-bottom: 8px;
64
- font-size: 14px;
65
- }
86
+ .label { padding-bottom: 8px; }
87
+ .label-text-value { font-size: 14px; }
88
+ .label-note { transform: translateY(1px); }
66
89
  }
67
90
 
68
91
  .SInputBase.medium {
69
- .label {
70
- padding-bottom: 8px;
71
- font-size: 14px;
72
- }
92
+ .label { padding-bottom: 8px; }
93
+ .label-text-value { font-size: 14px; }
94
+ .label-note { transform: translateY(1px); }
73
95
  }
74
96
 
75
97
  .SInputBase.has-error {
76
- .label {
98
+ .label-text-value {
77
99
  color: var(--input-error-text-color);
78
100
  }
79
101
  }
80
102
 
81
103
  .label {
82
- display: block;
104
+ display: flex;
105
+ align-items: center;
83
106
  width: 100%;
84
107
  line-height: 16px;
85
108
  font-weight: 500;
86
- color: var(--input-label-color);
87
109
  cursor: pointer;
88
110
  transition: color 0.25s;
89
111
  }
90
112
 
91
- .note {
113
+ .label-text {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: 4px;
117
+ }
118
+
119
+ .label-text-value {
120
+ color: var(--input-label-color);
121
+ }
122
+
123
+ .label-text-info {
124
+ width: 16px;
125
+ height: 16px;
126
+ color: var(--c-text-2);
127
+ transition: color 0.25s;
128
+
129
+ .label-text:hover & {
130
+ color: var(--c-info-text);
131
+ }
132
+ }
133
+
134
+ .label-note {
92
135
  display: inline-block;
93
136
  margin-left: 8px;
94
137
  font-size: 12px;
@@ -10,6 +10,7 @@ type Size = 'mini' | 'small' | 'medium'
10
10
  const props = defineProps({
11
11
  size: { type: String as PropType<Size>, default: 'small' },
12
12
  label: { type: String, default: null },
13
+ info: { type: String, default: null },
13
14
  note: { type: String, default: null },
14
15
  help: { type: String, default: null },
15
16
  text: { type: String, required: true },
@@ -30,6 +31,7 @@ function emitChange() {
30
31
  :class="[size]"
31
32
  :label="label"
32
33
  :note="note"
34
+ :info="info"
33
35
  :help="help"
34
36
  :validation="validation"
35
37
  >
@@ -44,6 +46,7 @@ function emitChange() {
44
46
  <p class="text">{{ text }}</p>
45
47
  </div>
46
48
  </div>
49
+ <template v-if="$slots.info" #info><slot name="info" /></template>
47
50
  </SInputBase>
48
51
  </template>
49
52
 
@@ -14,6 +14,7 @@ const props = defineProps({
14
14
  size: { type: String as PropType<Size>, default: 'small' },
15
15
  name: { type: String, default: null },
16
16
  label: { type: String, default: null },
17
+ info: { type: String, default: null },
17
18
  note: { type: String, default: null },
18
19
  help: { type: String, default: null },
19
20
  options: { type: Array as PropType<CheckboxOption[]>, required: true },
@@ -42,6 +43,7 @@ function handleChange(value: unknown): void {
42
43
  :name="name"
43
44
  :label="label"
44
45
  :note="note"
46
+ :info="info"
45
47
  :help="help"
46
48
  >
47
49
  <div class="container">
@@ -55,6 +57,7 @@ function handleChange(value: unknown): void {
55
57
  </div>
56
58
  </div>
57
59
  </div>
60
+ <template v-if="$slots.info" #info><slot name="info" /></template>
58
61
  </SInputBase>
59
62
  </template>
60
63
 
@@ -8,6 +8,7 @@ import SInputBase from './SInputBase.vue'
8
8
  const props = defineProps<{
9
9
  name?: string
10
10
  label?: string
11
+ info?: string
11
12
  note?: string
12
13
  help?: string
13
14
  hideError?: boolean
@@ -42,6 +43,7 @@ function emitBlur() {
42
43
  :name="name"
43
44
  :label="label"
44
45
  :note="note"
46
+ :info="info"
45
47
  :help="help"
46
48
  :hide-error="hideError"
47
49
  :validation="validation"
@@ -69,6 +71,7 @@ function emitBlur() {
69
71
  >
70
72
  </DatePicker>
71
73
  </div>
74
+ <template v-if="$slots.info" #info><slot name="info" /></template>
72
75
  </SInputBase>
73
76
  </template>
74
77
 
@@ -38,6 +38,7 @@ export interface OptionAvatar extends OptionBase {
38
38
  const props = defineProps<{
39
39
  size?: Size
40
40
  label?: string
41
+ info?: string
41
42
  note?: string
42
43
  help?: string
43
44
  placeholder?: string
@@ -128,6 +129,7 @@ function handleArray(value: OptionValue) {
128
129
  :class="classes"
129
130
  :label="label"
130
131
  :note="note"
132
+ :info="info"
131
133
  :help="help"
132
134
  :validation="validation"
133
135
  >
@@ -163,6 +165,7 @@ function handleArray(value: OptionValue) {
163
165
  <SDropdown :sections="dropdownOptions" />
164
166
  </div>
165
167
  </div>
168
+ <template v-if="$slots.info" #info><slot name="info" /></template>
166
169
  </SInputBase>
167
170
  </template>
168
171
 
@@ -8,35 +8,44 @@ export type Size = 'mini' | 'small' | 'medium'
8
8
  const props = defineProps<{
9
9
  size?: Size
10
10
  label?: string
11
- text?: string
11
+ info?: string
12
12
  note?: string
13
13
  help?: string
14
+ text?: string
14
15
  placeholder?: string
15
- modelValue: File | null
16
+ value?: File | null
17
+ modelValue?: File | null
16
18
  hideError?: boolean
17
19
  validation?: Validatable
18
20
  }>()
19
21
 
20
22
  const emit = defineEmits<{
21
- (e: 'update:modelValue', file: File | null): void
23
+ (e: 'update:model-value', file: File | null): void
24
+ (e: 'change', file: File | null): void
22
25
  }>()
23
26
 
27
+ const _value = computed(() => {
28
+ return props.modelValue !== undefined
29
+ ? props.modelValue
30
+ : props.value !== undefined ? props.value : null
31
+ })
32
+
24
33
  const input = ref<HTMLInputElement | null>(null)
25
34
 
26
35
  const classes = computed(() => [props.size ?? 'small'])
27
36
 
28
- const fileName = computed(() => {
29
- return props.modelValue ? props.modelValue.name : null
30
- })
37
+ const fileName = computed(() => _value.value?.name ?? null)
31
38
 
32
- function open(): void {
39
+ /* c8 ignore next 4 */
40
+ function open() {
33
41
  input.value!.click()
34
42
  }
35
43
 
36
- function handleChange(e: Event): void {
44
+ function onChange(e: Event) {
37
45
  const file = (e.target as any).files[0]
38
46
 
39
- emit('update:modelValue', file ?? null)
47
+ emit('update:model-value', file ?? null)
48
+ emit('change', file ?? null)
40
49
 
41
50
  file && props.validation?.$touch()
42
51
  }
@@ -48,15 +57,16 @@ function handleChange(e: Event): void {
48
57
  :class="classes"
49
58
  :label="label"
50
59
  :note="note"
60
+ :info="info"
51
61
  :help="help"
52
- :hide-error="hideError"
53
62
  :validation="validation"
63
+ :hide-error="hideError"
54
64
  >
55
65
  <input
56
66
  ref="input"
57
67
  class="input"
58
68
  type="file"
59
- @change="handleChange"
69
+ @change="onChange"
60
70
  >
61
71
 
62
72
  <div class="box" role="button" @click="open">
@@ -71,13 +81,14 @@ function handleChange(e: Event): void {
71
81
  <p v-else-if="placeholder" class="placeholder">{{ placeholder }}</p>
72
82
  </div>
73
83
  </div>
84
+ <template v-if="$slots.info" #info><slot name="info" /></template>
74
85
  </SInputBase>
75
86
  </template>
76
87
 
77
88
  <style lang="postcss" scoped>
78
89
  .SInputFile.mini {
79
90
  .action {
80
- padding: 2px 8px 2px 2px;
91
+ padding: 3px 8px 3px 3px;
81
92
  }
82
93
 
83
94
  .button {
@@ -90,7 +101,7 @@ function handleChange(e: Event): void {
90
101
  .file-name,
91
102
  .placeholder {
92
103
  line-height: 30px;
93
- font-size: 12px;
104
+ font-size: var(--input-font-size, var(--input-mini-font-size));
94
105
  font-weight: 500;
95
106
  }
96
107
  }
@@ -110,7 +121,8 @@ function handleChange(e: Event): void {
110
121
  .file-name,
111
122
  .placeholder {
112
123
  line-height: 38px;
113
- font-size: 14px;
124
+ font-size: var(--input-font-size, var(--input-small-font-size));
125
+ font-weight: 400;
114
126
  }
115
127
 
116
128
  .placeholder {
@@ -133,7 +145,8 @@ function handleChange(e: Event): void {
133
145
  .file-name,
134
146
  .placeholder {
135
147
  line-height: 46px;
136
- font-size: 14px;
148
+ font-size: var(--input-font-size, var(--input-medium-font-size));
149
+ font-weight: 400;
137
150
  }
138
151
 
139
152
  .placeholder {
@@ -144,7 +157,7 @@ function handleChange(e: Event): void {
144
157
  .SInputFile.has-error {
145
158
  .box,
146
159
  .box:hover {
147
- border-color: var(--c-danger);
160
+ border-color: var(--input-error-border-color);
148
161
  }
149
162
  }
150
163
 
@@ -154,22 +167,14 @@ function handleChange(e: Event): void {
154
167
 
155
168
  .box {
156
169
  display: flex;
157
- border: 1px solid var(--c-divider);
158
- border-radius: 4px;
159
- background-color: var(--c-bg);
170
+ border: 1px solid var(--input-border-color);
171
+ border-radius: 6px;
172
+ background-color: var(--input-bg-color);
160
173
  cursor: pointer;
161
- transition: border-color .25s;
174
+ transition: border-color 0.25s;
162
175
 
163
176
  &:hover {
164
- border-color: var(--c-black);
165
-
166
- .button {
167
- background-color: var(--c-bg-soft);
168
- }
169
- }
170
-
171
- .dark &:hover {
172
- border-color: var(--c-gray);
177
+ border-color: var(--input-hover-border-color);
173
178
  }
174
179
  }
175
180
 
@@ -178,11 +183,11 @@ function handleChange(e: Event): void {
178
183
  }
179
184
 
180
185
  .button {
181
- border: 1px solid var(--c-divider-light);
186
+ border: 1px solid var(--c-divider);
182
187
  border-radius: 4px;
183
188
  color: var(--c-text-1);
184
- background-color: var(--c-bg-mute);
185
- transition: background-color .25s;
189
+ background-color: var(--c-mute);
190
+ transition: background-color 0.25s;
186
191
  }
187
192
 
188
193
  .text {
@@ -201,8 +206,8 @@ function handleChange(e: Event): void {
201
206
  text-overflow: ellipsis;
202
207
  }
203
208
 
204
- .file-name,
205
209
  .placeholder {
206
- color: var(--c-text-2);
210
+ font-weight: 500;
211
+ color: var(--input-placeholder-color)
207
212
  }
208
213
  </style>
@@ -16,6 +16,7 @@ export type ValueType = 'hour' | 'minute' | 'second'
16
16
  const props = defineProps<{
17
17
  size?: Size
18
18
  label?: string
19
+ info?: string
19
20
  note?: string
20
21
  help?: string
21
22
  noHour?: boolean
@@ -111,6 +112,7 @@ function createRequiredTouched(): boolean[] {
111
112
  :class="[size, { disabled }]"
112
113
  :label="label"
113
114
  :note="note"
115
+ :info="info"
114
116
  :help="help"
115
117
  :hide-error="hideError"
116
118
  :validation="validation"
@@ -147,9 +149,8 @@ function createRequiredTouched(): boolean[] {
147
149
  >
148
150
  </div>
149
151
 
150
- <template #before-help>
151
- <slot name="before-help" />
152
- </template>
152
+ <template #before-help><slot name="before-help" /></template>
153
+ <template v-if="$slots.info" #info><slot name="info" /></template>
153
154
  </SInputBase>
154
155
  </template>
155
156
 
@@ -11,6 +11,7 @@ const props = defineProps<{
11
11
  size?: Size
12
12
  name?: string
13
13
  label?: string
14
+ info?: string
14
15
  note?: string
15
16
  help?: string
16
17
  placeholder?: string
@@ -70,6 +71,7 @@ function emitUpdate(value: string | null) {
70
71
  type="number"
71
72
  :label="label"
72
73
  :note="note"
74
+ :info="info"
73
75
  :help="help"
74
76
  :align="align"
75
77
  :placeholder="placeholder"
@@ -80,5 +82,7 @@ function emitUpdate(value: string | null) {
80
82
  :validation="validation"
81
83
  @update:model-value="emitUpdate"
82
84
  @input="emitUpdate"
83
- />
85
+ >
86
+ <template v-if="$slots.info" #info><slot name="info" /></template>
87
+ </SInputText>
84
88
  </template>
@@ -8,20 +8,23 @@ const props = defineProps<{
8
8
  size?: Size
9
9
  name?: string
10
10
  label?: string
11
+ info?: string
11
12
  note?: string
12
13
  help?: string
13
14
  text: string
14
- hideError?: boolean
15
15
  modelValue: boolean
16
16
  validation?: Validatable
17
+ hideError?: boolean
17
18
  }>()
18
19
 
19
20
  const emit = defineEmits<{
20
- (e: 'update:modelValue', value: boolean): void
21
+ (e: 'update:model-value', value: boolean): void
22
+ (e: 'change', value: boolean): void
21
23
  }>()
22
24
 
23
- function emitChange() {
24
- emit('update:modelValue', !props.modelValue)
25
+ function onClick() {
26
+ emit('update:model-value', !props.modelValue)
27
+ emit('change', !props.modelValue)
25
28
  }
26
29
  </script>
27
30
 
@@ -31,12 +34,13 @@ function emitChange() {
31
34
  :class="[size ?? 'small']"
32
35
  :label="label"
33
36
  :note="note"
37
+ :info="info"
34
38
  :help="help"
35
- :hide-error="hideError"
36
39
  :validation="validation"
40
+ :hide-error="hideError"
37
41
  >
38
42
  <div class="container">
39
- <div class="input" :class="{ on: modelValue }" role="button" @click="emitChange">
43
+ <div class="input" :class="{ on: props.modelValue }" role="button" @click="onClick">
40
44
  <div class="box">
41
45
  <div class="check" />
42
46
  </div>
@@ -44,6 +48,7 @@ function emitChange() {
44
48
  <p class="text">{{ text }}</p>
45
49
  </div>
46
50
  </div>
51
+ <template v-if="$slots.info" #info><slot name="info" /></template>
47
52
  </SInputBase>
48
53
  </template>
49
54
 
@@ -57,34 +62,34 @@ function emitChange() {
57
62
  display: flex;
58
63
  align-items: center;
59
64
  height: 32px;
65
+ cursor: pointer;
60
66
 
61
67
  &:hover {
62
68
  .box {
63
- border-color: var(--c-info);
69
+ border-color: var(--c-info-light);
64
70
  }
65
71
  }
66
72
  }
67
73
 
68
74
  .input.on {
69
75
  .box {
70
- border-color: var(--c-info);
71
- box-shadow: var(--shadow-depth-3);
76
+ border-color: var(--c-info-light);
72
77
  }
73
78
 
74
79
  .check {
75
80
  opacity: 1;
76
- transform: scale(.6);
81
+ transform: scale(0.6);
77
82
  }
78
83
  }
79
84
 
80
85
  .box {
81
86
  position: relative;
82
- border: 1px solid var(--c-divider);
87
+ border: 1px solid var(--c-divider-1);
83
88
  border-radius: 50%;
84
89
  width: 16px;
85
90
  height: 16px;
86
- background-color: var(--c-bg);
87
- transition: border-color 0.25s, box-shadow 0.25s;
91
+ background-color: var(--input-bg-color);
92
+ transition: border-color 0.25s;
88
93
  }
89
94
 
90
95
  .check {
@@ -98,7 +103,7 @@ function emitChange() {
98
103
  align-items: center;
99
104
  border-radius: 50%;
100
105
  width: 100%;
101
- background-color: var(--c-info);
106
+ background-color: var(--c-info-bg);
102
107
  opacity: 0;
103
108
  transform: scale(0);
104
109
  transition: opacity 0.25s, transform 0.1s;