@globalbrain/sefirot 2.14.1 → 2.15.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.
@@ -1,9 +1,11 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
2
+ import { IconifyIcon } from '@iconify/vue/dist/offline'
3
+ import { DefineComponent, computed } from 'vue'
3
4
  import { Validatable } from '../composables/Validation'
4
5
  import SInputBase from './SInputBase.vue'
5
6
 
6
7
  export type Size = 'mini' | 'small' | 'medium'
8
+ export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
7
9
 
8
10
  const props = defineProps<{
9
11
  size?: Size
@@ -13,6 +15,9 @@ const props = defineProps<{
13
15
  note?: string
14
16
  text?: string
15
17
  help?: string
18
+ checkIcon?: IconifyIcon | DefineComponent
19
+ checkText?: string
20
+ checkColor?: Color
16
21
  disabled?: boolean
17
22
  modelValue: boolean
18
23
  hideError?: boolean
@@ -41,6 +46,9 @@ function emitChange(): void {
41
46
  :label="label"
42
47
  :note="note"
43
48
  :info="info"
49
+ :check-icon="checkIcon"
50
+ :check-text="checkText"
51
+ :check-color="checkColor"
44
52
  :help="help"
45
53
  :hide-error="hideError"
46
54
  >
@@ -1,10 +1,12 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
2
+ import { IconifyIcon } from '@iconify/vue/dist/offline'
3
+ import { DefineComponent, computed } from 'vue'
3
4
  import { Validatable } from '../composables/Validation'
4
5
  import SInputBase from './SInputBase.vue'
5
6
  import SInputSwitch from './SInputSwitch.vue'
6
7
 
7
8
  export type Size = 'mini' | 'small' | 'medium'
9
+ export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
8
10
 
9
11
  export interface Option {
10
12
  label: string
@@ -18,6 +20,9 @@ const props = defineProps<{
18
20
  info?: string
19
21
  note?: string
20
22
  help?: string
23
+ checkIcon?: IconifyIcon | DefineComponent
24
+ checkText?: string
25
+ checkColor?: Color
21
26
  options: Option[]
22
27
  disabled?: boolean
23
28
  modelValue: (string | number | boolean)[]
@@ -55,6 +60,9 @@ function handleChange(value: string | number | boolean): void {
55
60
  :note="note"
56
61
  :info="info"
57
62
  :help="help"
63
+ :check-icon="checkIcon"
64
+ :check-text="checkText"
65
+ :check-color="checkColor"
58
66
  :hide-error="hideError"
59
67
  >
60
68
  <div class="container">
@@ -1,11 +1,13 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref } from 'vue'
2
+ import { IconifyIcon } from '@iconify/vue/dist/offline'
3
+ import { DefineComponent, computed, ref } from 'vue'
3
4
  import { Validatable } from '../composables/Validation'
4
5
  import SIcon from './SIcon.vue'
5
6
  import SInputBase from './SInputBase.vue'
6
7
 
7
8
  export type Size = 'mini' | 'small' | 'medium'
8
9
  export type Align = 'left' | 'center' | 'right'
10
+ export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
9
11
 
10
12
  const props = defineProps<{
11
13
  size?: Size
@@ -16,6 +18,9 @@ const props = defineProps<{
16
18
  help?: string
17
19
  type?: string
18
20
  placeholder?: string
21
+ checkIcon?: IconifyIcon | DefineComponent
22
+ checkText?: string
23
+ checkColor?: Color
19
24
  icon?: any
20
25
  align?: Align
21
26
  disabled?: boolean
@@ -26,7 +31,7 @@ const props = defineProps<{
26
31
  }>()
27
32
 
28
33
  const emit = defineEmits<{
29
- (e: 'update:modelValue', value: string | null): void
34
+ (e: 'update:model-value', value: string | null): void
30
35
  (e: 'input', value: string | null): void
31
36
  (e: 'blur', value: string | null): void
32
37
  (e: 'enter', value: string | null): void
@@ -58,7 +63,7 @@ function emitBlur(e: FocusEvent): void {
58
63
 
59
64
  props.validation?.$touch()
60
65
 
61
- emit('update:modelValue', value)
66
+ emit('update:model-value', value)
62
67
  emit('blur', value)
63
68
 
64
69
  isFocused.value = false
@@ -66,7 +71,7 @@ function emitBlur(e: FocusEvent): void {
66
71
 
67
72
  function emitInput(e: Event): void {
68
73
  const v = getValue(e)
69
- emit('update:modelValue', v)
74
+ emit('update:model-value', v)
70
75
  emit('input', v)
71
76
  }
72
77
 
@@ -75,7 +80,7 @@ function emitEnter(e: KeyboardEvent): void {
75
80
 
76
81
  props.validation?.$touch()
77
82
 
78
- emit('update:modelValue', value)
83
+ emit('update:model-value', value)
79
84
  emit('enter', value)
80
85
  }
81
86
 
@@ -95,17 +100,24 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
95
100
  :note="note"
96
101
  :info="info"
97
102
  :help="help"
103
+ :check-icon="checkIcon"
104
+ :check-text="checkText"
105
+ :check-color="checkColor"
98
106
  :hide-error="hideError"
99
107
  :validation="validation"
100
108
  >
101
109
  <div class="box" :class="{ focus: isFocused }" @click="focus">
110
+ <div v-if="$slots['addon-before']" class="addon before">
111
+ <slot name="addon-before" />
112
+ </div>
113
+
102
114
  <div v-if="icon" class="icon">
103
115
  <SIcon :icon="icon" class="icon-svg" />
104
116
  </div>
105
117
 
106
118
  <div class="value">
107
119
  <input
108
- class="input"
120
+ class="input entity"
109
121
  :class="{ hide: showDisplay }"
110
122
  :id="name"
111
123
  :type="type ?? 'text'"
@@ -118,38 +130,29 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
118
130
  @input="emitInput"
119
131
  @keypress.enter="emitEnter"
120
132
  >
121
- <div v-if="showDisplay" class="display">
133
+ <div v-if="showDisplay" class="input display">
122
134
  {{ displayValue }}
123
135
  </div>
124
136
  </div>
137
+
138
+ <div v-if="$slots['addon-after']" class="addon after">
139
+ <slot name="addon-after" />
140
+ </div>
125
141
  </div>
126
142
  <template v-if="$slots.info" #info><slot name="info" /></template>
127
143
  </SInputBase>
128
144
  </template>
129
145
 
130
- <style lang="postcss" scoped>
146
+ <style scoped lang="postcss">
131
147
  .SInputText.mini {
132
- .box {
133
- padding: 0 8px;
134
- min-height: 32px;
135
- }
148
+ .box { min-height: 32px; }
149
+ .value { min-height: 30px; }
136
150
 
137
- .value,
138
- .input,
139
- .display {
140
- min-height: 30px;
141
- }
142
-
143
- .input,
144
- .display {
145
- padding: 3px 0;
151
+ .input {
152
+ padding: 3px 8px;
146
153
  letter-spacing: 0;
147
154
  line-height: 24px;
148
- font-size: 14px;
149
- }
150
-
151
- .display {
152
- top: 0;
155
+ font-size: var(--input-font-size, var(--input-mini-font-size));
153
156
  }
154
157
 
155
158
  .icon {
@@ -161,30 +164,35 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
161
164
  width: 16px;
162
165
  height: 16px;
163
166
  }
164
- }
165
167
 
166
- .SInputText.small {
167
- .box {
168
- padding: 0 12px;
169
- min-height: 40px;
168
+ :slotted(.SInputAddon) .action {
169
+ padding: 0 10px;
170
+ font-size: 12px;
171
+ font-weight: 500;
170
172
  }
171
173
 
172
- .value,
173
- .input,
174
- .display {
175
- min-height: 38px;
174
+ :slotted(.SInputAddon) .action-icon {
175
+ width: 16px;
176
+ height: 16px;
176
177
  }
177
178
 
178
- .input,
179
- .display {
180
- padding: 5px 0;
181
- letter-spacing: 0;
182
- line-height: 24px;
183
- font-size: 16px;
179
+ :slotted(.SInputAddon) .caret {
180
+ margin-left: 4px;
181
+ margin-right: -2px;
182
+ width: 10px;
183
+ height: 10px;
184
184
  }
185
+ }
185
186
 
186
- .display {
187
- top: 2px;
187
+ .SInputText.small {
188
+ .box { min-height: 40px; }
189
+ .value { min-height: 38px; }
190
+
191
+ .input {
192
+ padding: 7px 12px;
193
+ letter-spacing: 0;
194
+ line-height: 24px;
195
+ font-size: var(--input-font-size, var(--input-small-font-size));
188
196
  }
189
197
 
190
198
  .icon {
@@ -196,30 +204,35 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
196
204
  width: 16px;
197
205
  height: 16px;
198
206
  }
199
- }
200
207
 
201
- .SInputText.medium {
202
- .box {
208
+ :slotted(.SInputAddon) .action {
203
209
  padding: 0 12px;
204
- min-height: 48px;
210
+ font-size: 14px;
211
+ font-weight: 500;
212
+ }
213
+
214
+ :slotted(.SInputAddon) .action-icon {
215
+ width: 16px;
216
+ height: 16px;
205
217
  }
206
218
 
207
- .value,
208
- .input,
209
- .display {
210
- min-height: 46px;
219
+ :slotted(.SInputAddon) .caret {
220
+ margin-left: 6px;
221
+ margin-right: -2px;
222
+ width: 12px;
223
+ height: 12px;
211
224
  }
225
+ }
226
+
227
+ .SInputText.medium {
228
+ .box { min-height: 48px; }
229
+ .value { min-height: 46px; }
212
230
 
213
- .input,
214
- .display {
215
- padding: 11px 0;
231
+ .input {
232
+ padding: 11px 12px;
216
233
  letter-spacing: 0;
217
234
  line-height: 24px;
218
- font-size: 16px;
219
- }
220
-
221
- .display {
222
- top: 0;
235
+ font-size: var(--input-font-size, var(--input-medium-font-size));
223
236
  }
224
237
 
225
238
  .icon {
@@ -231,48 +244,51 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
231
244
  width: 18px;
232
245
  height: 18px;
233
246
  }
234
- }
235
247
 
236
- .SInputText.disabled {
237
- .input {
238
- background-color: var(--input-outlined-bg-disabled);
248
+ :slotted(.SInputAddon) .action {
249
+ padding: 0 14px;
250
+ font-size: 14px;
251
+ font-weight: 500;
239
252
  }
240
253
 
241
- .box:hover .input {
242
- cursor: not-allowed;
243
- border-color: var(--input-outlined-border);
254
+ :slotted(.SInputAddon) .action-icon {
255
+ width: 16px;
256
+ height: 16px;
244
257
  }
245
- }
246
258
 
247
- .SInputText.left {
248
- .input,
249
- .display {
250
- text-align: left;
259
+ :slotted(.SInputAddon) .caret {
260
+ margin-left: 6px;
261
+ margin-right: -2px;
262
+ width: 12px;
263
+ height: 12px;
251
264
  }
252
265
  }
253
266
 
254
- .SInputText.center {
255
- .input,
256
- .display {
257
- text-align: center;
267
+ .SInputText.disabled {
268
+ .box,
269
+ .box:hover,
270
+ .box:hover:has(.SInputAddon:is(.focused, :hover)),
271
+ .box:hover.focus,
272
+ .box.focus:has(.SInputAddon:is(.focused, :hover)),
273
+ .box:hover.focus:has(.SInputAddon:is(.focused, :hover)) {
274
+ border-color: var(--input-disabled-border-color);
275
+ background-color: var(--input-disabled-bg-color);
258
276
  }
259
- }
260
277
 
261
- .SInputText.right {
262
- .input,
263
- .display {
264
- text-align: right;
278
+ .box:hover .input {
279
+ cursor: not-allowed;
265
280
  }
266
281
  }
267
282
 
268
- .SInputText.has-error {
269
- .box {
270
- border-color: var(--c-danger);
283
+ .SInputText.left .input { text-align: left; }
284
+ .SInputText.center .input { text-align: center; }
285
+ .SInputText.right .input { text-align: right; }
271
286
 
272
- &:hover,
273
- &:focus {
274
- border-color: var(--c-danger);
275
- }
287
+ .SInputText.has-error {
288
+ .box,
289
+ .box:hover,
290
+ .box:focus {
291
+ border-color: var(--input-error-border-color);
276
292
  }
277
293
  }
278
294
 
@@ -281,28 +297,24 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
281
297
  display: flex;
282
298
  flex-grow: 1;
283
299
  max-width: 100%;
284
- border: 1px solid var(--c-divider);
300
+ border: 1px solid var(--input-border-color);
285
301
  border-radius: 6px;
286
- background-color: var(--c-bg);
287
- cursor: text;
302
+ background-color: var(--input-bg-color);
288
303
  transition: border-color 0.25s;
289
304
 
290
305
  &:hover {
291
- border-color: var(--c-black);
306
+ border-color: var(--input-hover-border-color);
292
307
  }
293
308
 
294
- &.focus,
295
- &:hover.focus {
296
- border-color: var(--c-info);
309
+ &:hover:has(.SInputAddon:is(.focused, :hover)) {
310
+ border-color: var(--input-border-color);
297
311
  }
298
312
 
299
- .dark &:hover {
300
- border-color: var(--c-gray);
301
- }
302
-
303
- .dark &.focus,
304
- .dark &:hover.focus {
305
- border-color: var(--c-info);
313
+ &.focus,
314
+ &:hover.focus,
315
+ &.focus:has(.SInputAddon:is(.focused, :hover)),
316
+ &:hover.focus:has(.SInputAddon:is(.focused, :hover)) {
317
+ border-color: var(--input-focus-border-color);
306
318
  }
307
319
  }
308
320
 
@@ -312,8 +324,15 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
312
324
  }
313
325
 
314
326
  .input {
327
+ position: absolute;
328
+ top: 0;
329
+ right: 0;
330
+ bottom: 0;
331
+ left: 0;
315
332
  width: 100%;
333
+ color: var(--input-value-color);
316
334
  background-color: transparent;
335
+ cursor: text;
317
336
 
318
337
  &.hide,
319
338
  &.hide::placeholder {
@@ -321,13 +340,15 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
321
340
  }
322
341
 
323
342
  &::placeholder {
324
- font-weight: 500;
325
- color: var(--c-text-3);
343
+ color: var(--input-placeholder-color);
326
344
  }
327
345
  }
328
346
 
329
347
  .display {
330
348
  position: absolute;
349
+ top: 0;
350
+ right: 0;
351
+ bottom: 0;
331
352
  left: 0;
332
353
  width: 100%;
333
354
  }
@@ -342,4 +363,35 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
342
363
  .icon-svg {
343
364
  fill: currentColor;
344
365
  }
366
+
367
+ .addon {
368
+ display: flex;
369
+ flex-shrink: 0;
370
+ }
371
+
372
+ .addon :slotted(.SInputAddon) .caret {
373
+ color: var(--c-text-2);
374
+ }
375
+
376
+ .addon.before :slotted(.SInputAddon) {
377
+ .action {
378
+ border-right: 1px solid var(--input-border-color);
379
+ border-radius: 5px 0 0 5px;
380
+ }
381
+
382
+ .dialog {
383
+ left: 0;
384
+ }
385
+ }
386
+
387
+ .addon.after :slotted(.SInputAddon) {
388
+ .action {
389
+ border-left: 1px solid var(--input-border-color);
390
+ border-radius: 0 5px 5px 0;
391
+ }
392
+
393
+ .dialog {
394
+ right: 0;
395
+ }
396
+ }
345
397
  </style>
@@ -1,9 +1,11 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
2
+ import { IconifyIcon } from '@iconify/vue/dist/offline'
3
+ import { DefineComponent, computed } from 'vue'
3
4
  import { Validatable } from '../composables/Validation'
4
5
  import SInputBase from './SInputBase.vue'
5
6
 
6
7
  export type Size = 'mini' | 'small' | 'medium'
8
+ export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
7
9
 
8
10
  const props = defineProps<{
9
11
  size?: Size
@@ -12,6 +14,9 @@ const props = defineProps<{
12
14
  info?: string
13
15
  note?: string
14
16
  help?: string
17
+ checkIcon?: IconifyIcon | DefineComponent
18
+ checkText?: string
19
+ checkColor?: Color
15
20
  placeholder?: string
16
21
  disabled?: boolean
17
22
  rows?: number
@@ -48,6 +53,9 @@ function emitBlur(e: FocusEvent): void {
48
53
  :note="note"
49
54
  :info="info"
50
55
  :help="help"
56
+ :check-icon="checkIcon"
57
+ :check-text="checkText"
58
+ :check-color="checkColor"
51
59
  :hide-error="hideError"
52
60
  :validation="validation"
53
61
  >
@@ -1,9 +1,11 @@
1
1
  <script setup lang="ts">
2
- import { ref } from 'vue'
2
+ import { IconifyIcon } from '@iconify/vue/dist/offline'
3
+ import { DefineComponent, ref } from 'vue'
3
4
  import { Validatable } from '../composables/Validation'
4
5
  import SInputBase from './SInputBase.vue'
5
6
 
6
7
  export type Size = 'mini' | 'small' | 'medium'
8
+ export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
7
9
 
8
10
  export interface Value {
9
11
  year: number | null
@@ -19,6 +21,9 @@ const props = defineProps<{
19
21
  info?: string
20
22
  note?: string
21
23
  help?: string
24
+ checkIcon?: IconifyIcon | DefineComponent
25
+ checkText?: string
26
+ checkColor?: Color
22
27
  noYear?: boolean
23
28
  noMonth?: boolean
24
29
  noDate?: boolean
@@ -104,6 +109,9 @@ function createRequiredTouched(): boolean[] {
104
109
  :note="note"
105
110
  :info="info"
106
111
  :help="help"
112
+ :check-icon="checkIcon"
113
+ :check-text="checkText"
114
+ :check-color="checkColor"
107
115
  :hide-error="hideError"
108
116
  :validation="validation"
109
117
  >
@@ -1,4 +1,5 @@
1
- import { MaybeRef } from '@vueuse/core'
1
+ import { MaybeRef, useElementBounding, useWindowSize } from '@vueuse/core'
2
+ import { Ref, ref, unref } from 'vue'
2
3
 
3
4
  export type DropdownSection =
4
5
  | DropdownSectionMenu
@@ -55,6 +56,70 @@ export interface DropdownSectionFilterOptionAvatar extends DropdownSectionFilter
55
56
  image?: string | null
56
57
  }
57
58
 
59
+ export interface ManualDropdownPosition {
60
+ position: Ref<'top' | 'bottom'>
61
+ update(): void
62
+ }
63
+
58
64
  export function createDropdown(section: DropdownSection[]): DropdownSection[] {
59
65
  return section
60
66
  }
67
+
68
+ export function useManualDropdownPosition(
69
+ container?: Ref<any>,
70
+ initPosition?: 'top' | 'bottom'
71
+ ): ManualDropdownPosition {
72
+ const el = container ?? ref<any>(null)
73
+
74
+ const { top, bottom } = useElementBounding(el)
75
+ const { height } = useWindowSize()
76
+
77
+ const position = ref<'top' | 'bottom'>('bottom')
78
+
79
+ const dialogHeight = 400
80
+
81
+ function update(): void {
82
+ if (initPosition) {
83
+ return
84
+ }
85
+
86
+ // If the space top of the input is not enough to show dialog, just show
87
+ // the dialog at the bottom of the input.
88
+ if (top.value < dialogHeight) {
89
+ position.value = 'bottom'
90
+ return
91
+ }
92
+
93
+ // Else show dialog depending on the space bottom of the input.
94
+ if (bottom.value + dialogHeight <= height.value) {
95
+ position.value = 'bottom'
96
+ return
97
+ }
98
+
99
+ position.value = 'top'
100
+ }
101
+
102
+ return {
103
+ position,
104
+ update
105
+ }
106
+ }
107
+
108
+ export function getSelectedOption(
109
+ sections: DropdownSection[]
110
+ ): DropdownSectionFilterOption | null {
111
+ for (const section of sections) {
112
+ if (section.type === 'filter') {
113
+ const options = unref(section.options)
114
+ const selected = unref(section.selected)
115
+
116
+ for (const option of options) {
117
+ if (option.value === selected) {
118
+ return option
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ return null
125
+ }
@@ -1,7 +1,7 @@
1
- import { ref, watch } from 'vue'
1
+ import { Ref, ref, watch } from 'vue'
2
2
 
3
- export function useFlyout() {
4
- const container = ref<any>(null)
3
+ export function useFlyout(container?: Ref<any>) {
4
+ const el = container ?? ref<any>(null)
5
5
 
6
6
  const isOpen = ref(false)
7
7
 
@@ -18,7 +18,7 @@ export function useFlyout() {
18
18
  }
19
19
 
20
20
  function closeOnClickOutside(event: any) {
21
- if (!container.value.contains(event.target) && isVisible(container.value)) {
21
+ if (!el.value.contains(event.target) && isVisible(el.value)) {
22
22
  isOpen.value = false
23
23
  }
24
24
  }
@@ -37,7 +37,7 @@ export function useFlyout() {
37
37
  })
38
38
 
39
39
  return {
40
- container,
40
+ container: el,
41
41
  isOpen,
42
42
  open,
43
43
  close,
@@ -651,6 +651,7 @@
651
651
  --input-focus-border-color: var(--c-info-light);
652
652
  --input-error-text-color: var(--c-danger-text);
653
653
  --input-error-border-color: var(--c-danger-light);
654
+ --input-disabled-border-color: var(--c-divider-1);
654
655
  --input-disabled-value-color: var(--c-text-1);
655
656
  --input-disabled-bg-color: var(--c-mute);
656
657