@globalbrain/sefirot 2.12.0 → 2.14.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.
Files changed (37) hide show
  1. package/LICENSE.md +1 -1
  2. package/lib/components/SButton.vue +4 -2
  3. package/lib/components/SInputBase.vue +75 -32
  4. package/lib/components/SInputCheckbox.vue +3 -0
  5. package/lib/components/SInputCheckboxes.vue +3 -0
  6. package/lib/components/SInputDate.vue +3 -0
  7. package/lib/components/SInputDropdown.vue +3 -0
  8. package/lib/components/SInputFile.vue +39 -34
  9. package/lib/components/SInputHMS.vue +4 -3
  10. package/lib/components/SInputNumber.vue +5 -1
  11. package/lib/components/SInputRadio.vue +19 -14
  12. package/lib/components/SInputRadios.vue +38 -12
  13. package/lib/components/SInputSelect.vue +15 -17
  14. package/lib/components/SInputSwitch.vue +3 -0
  15. package/lib/components/SInputSwitches.vue +3 -0
  16. package/lib/components/SInputText.vue +3 -0
  17. package/lib/components/SInputTextarea.vue +3 -0
  18. package/lib/components/SInputYMD.vue +3 -0
  19. package/lib/components/STable.vue +39 -10
  20. package/lib/components/STableColumn.vue +20 -5
  21. package/lib/components/STableFooter.vue +14 -8
  22. package/lib/components/STableHeader.vue +13 -7
  23. package/lib/components/STooltip.vue +51 -52
  24. package/lib/composables/Table.ts +5 -0
  25. package/lib/composables/Tooltip.ts +3 -3
  26. package/lib/composables/Utils.ts +26 -0
  27. package/lib/composables/Validation.ts +5 -2
  28. package/lib/styles/variables.css +13 -1
  29. package/lib/support/Day.ts +23 -0
  30. package/lib/support/Time.ts +2 -6
  31. package/lib/validation/rules/index.ts +6 -6
  32. package/lib/validation/rules/maxFileSize.ts +16 -0
  33. package/lib/validation/validators/maxFileSize.ts +10 -0
  34. package/package.json +10 -10
  35. package/lib/support/Day/Constant.ts +0 -32
  36. package/lib/support/Day/index.ts +0 -11
  37. package/lib/support/Day/plugins/RelativeTime.ts +0 -124
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import { computed } from 'vue'
2
3
  import { Validatable } from '../composables/Validation'
3
4
  import SInputBase from './SInputBase.vue'
4
5
  import SInputRadio from './SInputRadio.vue'
@@ -10,36 +11,58 @@ export interface Option {
10
11
  value: string | number | boolean
11
12
  }
12
13
 
13
- const props = defineProps<{
14
+ const props = withDefaults(defineProps<{
14
15
  size?: Size
15
16
  name?: string
16
17
  label?: string
18
+ info?: string
17
19
  note?: string
18
20
  help?: string
19
- nullable?: boolean
20
21
  options: Option[]
21
- hideError?: boolean
22
- modelValue: string | number | boolean | null
22
+ nullable?: boolean
23
+ value?: string | number | boolean | null
24
+ modelValue?: string | number | boolean | null
23
25
  validation?: Validatable
24
- }>()
26
+ hideError?: boolean
27
+ }>(), {
28
+ value: undefined,
29
+ modelValue: undefined
30
+ })
25
31
 
26
32
  const emit = defineEmits<{
27
- (e: 'update:modelValue', value: string | number | boolean | null): void
33
+ (e: 'update:model-value', value: string | number | boolean | null): void
34
+ (e: 'change', value: string | number | boolean | null): void
28
35
  }>()
29
36
 
37
+ const _value = computed(() => {
38
+ return props.modelValue !== undefined
39
+ ? props.modelValue
40
+ : props.value !== undefined ? props.value : null
41
+ })
42
+
30
43
  function isChecked(value: string | number | boolean) {
31
- return value === props.modelValue
44
+ return value === _value.value
32
45
  }
33
46
 
34
- function handleChange(value: string | number | boolean) {
35
- if (value !== props.modelValue) {
36
- emit('update:modelValue', value)
47
+ function onUpdate(value: string | number | boolean) {
48
+ if (value !== _value.value) {
49
+ emit('update:model-value', value)
50
+ return
51
+ }
52
+
53
+ if (props.nullable) {
54
+ emit('update:model-value', null)
55
+ }
56
+ }
37
57
 
58
+ function onChange(value: string | number | boolean) {
59
+ if (value !== _value.value) {
60
+ emit('change', value)
38
61
  return
39
62
  }
40
63
 
41
64
  if (props.nullable) {
42
- emit('update:modelValue', null)
65
+ emit('change', null)
43
66
  }
44
67
  }
45
68
  </script>
@@ -50,6 +73,7 @@ function handleChange(value: string | number | boolean) {
50
73
  :class="[size ?? 'small']"
51
74
  :label="label"
52
75
  :note="note"
76
+ :info="info"
53
77
  :help="help"
54
78
  :hide-error="hideError"
55
79
  :validation="validation"
@@ -60,10 +84,12 @@ function handleChange(value: string | number | boolean) {
60
84
  <SInputRadio
61
85
  :text="option.label"
62
86
  :model-value="isChecked(option.value)"
63
- @update:model-value="handleChange(option.value)"
87
+ @update:model-value="onUpdate(option.value)"
88
+ @change="onChange(option.value)"
64
89
  />
65
90
  </div>
66
91
  </div>
67
92
  </div>
93
+ <template v-if="$slots.info" #info><slot name="info" /></template>
68
94
  </SInputBase>
69
95
  </template>
@@ -15,9 +15,10 @@ export interface Option {
15
15
  disabled?: boolean
16
16
  }
17
17
 
18
- const props = defineProps<{
18
+ const props = withDefaults(defineProps<{
19
19
  size?: Size
20
20
  label?: string
21
+ info?: string
21
22
  note?: string
22
23
  help?: string
23
24
  placeholder?: string
@@ -28,7 +29,10 @@ const props = defineProps<{
28
29
  modelValue?: Value
29
30
  validation?: Validatable
30
31
  hideError?: boolean
31
- }>()
32
+ }>(), {
33
+ value: undefined,
34
+ modelValue: undefined
35
+ })
32
36
 
33
37
  const emit = defineEmits<{
34
38
  (e: 'update:model-value', value: Value): void
@@ -49,27 +53,19 @@ const classes = computed(() => [
49
53
  ])
50
54
 
51
55
  const isNotSelected = computed(() => {
52
- return _value.value === undefined || _value.value === null || _value.value === ''
56
+ return _value.value == null || _value.value === ''
53
57
  })
54
58
 
55
59
  function isSelectedOption(option: Option): boolean {
56
60
  return option.value === _value.value
57
61
  }
58
62
 
59
- function focus() {
60
- isFocused.value = true
61
- }
62
-
63
- function blur() {
64
- isFocused.value = false
65
- }
66
-
67
63
  function emitChange(e: any): void {
68
64
  if (!props.disabled) {
69
65
  props.validation?.$touch()
70
66
 
71
67
  const input = e.target.value
72
- const value = input === '__null__' ? null : JSON.parse(input).value
68
+ const value = props.options[input]?.value ?? null
73
69
 
74
70
  emit('update:model-value', value)
75
71
  emit('change', value)
@@ -83,6 +79,7 @@ function emitChange(e: any): void {
83
79
  :class="classes"
84
80
  :label="label"
85
81
  :note="note"
82
+ :info="info"
86
83
  :help="help"
87
84
  :hide-error="hideError"
88
85
  :validation="validation"
@@ -92,14 +89,14 @@ function emitChange(e: any): void {
92
89
  class="select"
93
90
  :class="{ 'is-not-selected': isNotSelected }"
94
91
  :disabled="disabled"
95
- @focus="focus"
96
- @blur="blur"
92
+ @focus="isFocused = true"
93
+ @blur="isFocused = false"
97
94
  @change="emitChange"
98
95
  >
99
96
  <option
100
97
  v-if="placeholder || nullable"
101
98
  class="option"
102
- value="__null__"
99
+ value="-1"
103
100
  :selected="isNotSelected"
104
101
  :disabled="!nullable"
105
102
  >
@@ -107,11 +104,11 @@ function emitChange(e: any): void {
107
104
  </option>
108
105
 
109
106
  <option
110
- v-for="option in options"
107
+ v-for="option, index in options"
111
108
  :key="JSON.stringify(option)"
112
109
  :style="{ display: option.disabled ? 'none' : undefined }"
113
110
  class="option"
114
- :value="JSON.stringify(option)"
111
+ :value="index"
115
112
  :selected="isSelectedOption(option)"
116
113
  >
117
114
  {{ option.label }}
@@ -123,6 +120,7 @@ function emitChange(e: any): void {
123
120
  <SIcon :icon="IconCaretDown" class="icon-svg down" />
124
121
  </div>
125
122
  </div>
123
+ <template v-if="$slots.info" #info><slot name="info" /></template>
126
124
  </SInputBase>
127
125
  </template>
128
126
 
@@ -9,6 +9,7 @@ const props = defineProps<{
9
9
  size?: Size
10
10
  name?: string
11
11
  label?: string
12
+ info?: string
12
13
  note?: string
13
14
  text?: string
14
15
  help?: string
@@ -39,6 +40,7 @@ function emitChange(): void {
39
40
  :name="name"
40
41
  :label="label"
41
42
  :note="note"
43
+ :info="info"
42
44
  :help="help"
43
45
  :hide-error="hideError"
44
46
  >
@@ -51,6 +53,7 @@ function emitChange(): void {
51
53
  </div>
52
54
  </div>
53
55
  </div>
56
+ <template v-if="$slots.info" #info><slot name="info" /></template>
54
57
  </SInputBase>
55
58
  </template>
56
59
 
@@ -15,6 +15,7 @@ const props = defineProps<{
15
15
  size?: Size
16
16
  name?: string
17
17
  label?: string
18
+ info?: string
18
19
  note?: string
19
20
  help?: string
20
21
  options: Option[]
@@ -52,6 +53,7 @@ function handleChange(value: string | number | boolean): void {
52
53
  :name="name"
53
54
  :label="label"
54
55
  :note="note"
56
+ :info="info"
55
57
  :help="help"
56
58
  :hide-error="hideError"
57
59
  >
@@ -67,6 +69,7 @@ function handleChange(value: string | number | boolean): void {
67
69
  </div>
68
70
  </div>
69
71
  </div>
72
+ <template v-if="$slots.info" #info><slot name="info" /></template>
70
73
  </SInputBase>
71
74
  </template>
72
75
 
@@ -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
  type?: string
@@ -92,6 +93,7 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
92
93
  :name="name"
93
94
  :label="label"
94
95
  :note="note"
96
+ :info="info"
95
97
  :help="help"
96
98
  :hide-error="hideError"
97
99
  :validation="validation"
@@ -121,6 +123,7 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
121
123
  </div>
122
124
  </div>
123
125
  </div>
126
+ <template v-if="$slots.info" #info><slot name="info" /></template>
124
127
  </SInputBase>
125
128
  </template>
126
129
 
@@ -9,6 +9,7 @@ const props = defineProps<{
9
9
  size?: Size
10
10
  name?: string
11
11
  label?: string
12
+ info?: string
12
13
  note?: string
13
14
  help?: string
14
15
  placeholder?: string
@@ -45,6 +46,7 @@ function emitBlur(e: FocusEvent): void {
45
46
  :name="name"
46
47
  :label="label"
47
48
  :note="note"
49
+ :info="info"
48
50
  :help="help"
49
51
  :hide-error="hideError"
50
52
  :validation="validation"
@@ -59,6 +61,7 @@ function emitBlur(e: FocusEvent): void {
59
61
  @input="emitInput"
60
62
  @blur="emitBlur"
61
63
  />
64
+ <template v-if="$slots.info" #info><slot name="info" /></template>
62
65
  </SInputBase>
63
66
  </template>
64
67
 
@@ -16,6 +16,7 @@ export type ValueType = 'year' | 'month' | 'date'
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
  noYear?: boolean
@@ -101,6 +102,7 @@ function createRequiredTouched(): boolean[] {
101
102
  :class="[size ?? 'small', { disabled }]"
102
103
  :label="label"
103
104
  :note="note"
105
+ :info="info"
104
106
  :help="help"
105
107
  :hide-error="hideError"
106
108
  :validation="validation"
@@ -145,6 +147,7 @@ function createRequiredTouched(): boolean[] {
145
147
  @blur="updateDate"
146
148
  >
147
149
  </div>
150
+ <template v-if="$slots.info" #info><slot name="info" /></template>
148
151
  </SInputBase>
149
152
  </template>
150
153
 
@@ -16,6 +16,8 @@ const {
16
16
  orders,
17
17
  columns,
18
18
  records,
19
+ header,
20
+ footer,
19
21
  total,
20
22
  page,
21
23
  perPage,
@@ -36,11 +38,31 @@ let bodyLock = false
36
38
  const colWidths = reactive<Record<string, string>>({})
37
39
 
38
40
  const showHeader = computed(() => {
39
- return total?.value !== undefined
41
+ if (header?.value === true) {
42
+ return true
43
+ }
44
+
45
+ if (header?.value === false) {
46
+ return false
47
+ }
48
+
49
+ return total?.value !== undefined || !!reset?.value
40
50
  })
41
51
 
42
52
  const showFooter = computed(() => {
43
- return !loading?.value && page?.value && total?.value
53
+ if (loading?.value) {
54
+ return false
55
+ }
56
+
57
+ if (footer?.value === true) {
58
+ return true
59
+ }
60
+
61
+ if (footer?.value === false) {
62
+ return false
63
+ }
64
+
65
+ return page?.value && perPage?.value && total?.value
44
66
  })
45
67
 
46
68
  watch(() => records?.value, () => {
@@ -114,6 +136,8 @@ function updateColWidth(key: string, value: string) {
114
136
  :label="columns[key].label"
115
137
  :class-name="columns[key].className"
116
138
  :dropdown="columns[key].dropdown"
139
+ :has-header="showHeader"
140
+ :resizable="columns[key].resizable"
117
141
  @resize="(value) => updateColWidth(key, value)"
118
142
  />
119
143
  </STableItem>
@@ -180,12 +204,13 @@ function updateColWidth(key: string, value: string) {
180
204
  <style scoped lang="postcss">
181
205
  .box {
182
206
  position: relative;
183
- border-top: var(--table-border);
184
- border-right: var(--table-border);
185
- border-bottom: var(--table-border);
186
- border-left: var(--table-border);
207
+ border-top: var(--table-border-top, var(--table-border));
208
+ border-right: var(--table-border-right, var(--table-border));
209
+ border-bottom: var(--table-border-bottom, var(--table-border));
210
+ border-left: var(--table-border-left, var(--table-border));
187
211
  border-radius: var(--table-border-radius);
188
212
  width: 100%;
213
+ overflow: hidden;
189
214
 
190
215
  .STable.borderless & {
191
216
  border-right: 0;
@@ -210,7 +235,7 @@ function updateColWidth(key: string, value: string) {
210
235
  position: var(--table-head-position, static);
211
236
  top: var(--table-head-top, auto);
212
237
  z-index: 100;
213
- background-color: var(--bg-elv);
238
+ background-color: var(--bg-elv-2);
214
239
 
215
240
  &::-webkit-scrollbar {
216
241
  display: none;
@@ -225,14 +250,18 @@ function updateColWidth(key: string, value: string) {
225
250
 
226
251
  .row {
227
252
  display: flex;
228
- border-bottom: 1px solid var(--c-divider-light);
253
+ border-bottom: 1px solid var(--c-divider-2);
254
+
255
+ .body &:last-child {
256
+ border-bottom: 0;
257
+ }
229
258
  }
230
259
 
231
260
  .missing {
232
261
  border-radius: 0 0 6px 6px;
233
262
  padding: 48px 32px;
234
263
  text-align: center;
235
- background-color: var(--c-bg-elv-up);
264
+ background-color: var(--c-bg-elv-3);
236
265
  line-height: 24px;
237
266
  font-size: 14px;
238
267
  font-weight: 500;
@@ -242,7 +271,7 @@ function updateColWidth(key: string, value: string) {
242
271
  .loading {
243
272
  border-radius: 0 0 6px 6px;
244
273
  padding: 64px 32px;
245
- background-color: var(--c-bg-elv-up);
274
+ background-color: var(--c-bg-elv-3);
246
275
  }
247
276
 
248
277
  .loading-icon {
@@ -7,12 +7,16 @@ import { isArray } from '../support/Utils'
7
7
  import SDropdown from './SDropdown.vue'
8
8
  import SIcon from './SIcon.vue'
9
9
 
10
- const props = defineProps<{
10
+ const props = withDefaults(defineProps<{
11
11
  name: string
12
12
  label: string
13
13
  className?: string
14
14
  dropdown?: DropdownSection[]
15
- }>()
15
+ hasHeader: boolean
16
+ resizable?: boolean
17
+ }>(), {
18
+ resizable: true
19
+ })
16
20
 
17
21
  const emit = defineEmits<{
18
22
  (e: 'resize', value: string): void
@@ -54,6 +58,13 @@ const buttonActive = computed(() => {
54
58
  return isOpen.value || active.value
55
59
  })
56
60
 
61
+ const classes = computed(() => [
62
+ { active: active.value },
63
+ { 'has-header': props.hasHeader },
64
+ props.className,
65
+ `col-${props.name}`
66
+ ])
67
+
57
68
  watch(isOpen, (value) => {
58
69
  value ? adjustDialogPosition() : stopDialogPositionListener()
59
70
  })
@@ -112,7 +123,7 @@ function stopDialogPositionListener() {
112
123
  </script>
113
124
 
114
125
  <template>
115
- <div class="STableColumn STableCell" :class="[{ active }, className, `col-${name}`]" ref="column">
126
+ <div class="STableColumn STableCell" :class="classes" ref="column">
116
127
  <div class="container">
117
128
  <p class="label">{{ label }}</p>
118
129
 
@@ -128,16 +139,19 @@ function stopDialogPositionListener() {
128
139
  </transition>
129
140
  </div>
130
141
 
131
- <div class="grip" @mousedown="grip" />
142
+ <div v-if="resizable" class="grip" @mousedown="grip" />
132
143
  </div>
133
144
  </div>
134
145
  </template>
135
146
 
136
147
  <style scoped lang="postcss">
137
148
  .STableColumn {
138
- border-top: 1px solid var(--c-divider-light);
139
149
  background-color: var(--c-bg-soft);
140
150
 
151
+ &.has-header {
152
+ border-top: 1px solid var(--c-divider-2);
153
+ }
154
+
141
155
  .STableItem:first-child & {
142
156
  padding-left: var(--table-padding-left);
143
157
  }
@@ -156,6 +170,7 @@ function stopDialogPositionListener() {
156
170
 
157
171
  .label {
158
172
  flex-grow: 1;
173
+ margin: 0;
159
174
  line-height: 40px;
160
175
  text-align: left;
161
176
  font-size: 12px;
@@ -42,7 +42,7 @@ const hasNext = computed(() => {
42
42
  <div class="container">
43
43
  <p class="info">{{ format(from) }}–{{ format(to) }} of {{ format(_total) }}</p>
44
44
 
45
- <div class="actions">
45
+ <div v-if="onPrev && onNext" class="actions">
46
46
  <button class="button prev" :class="{ active: hasPrev }" @click="() => hasPrev && onPrev?.()">
47
47
  <SIcon :icon="IconCaretLeft" class="icon" />
48
48
  </button>
@@ -56,12 +56,11 @@ const hasNext = computed(() => {
56
56
 
57
57
  <style scoped lang="postcss">
58
58
  .STableFooter {
59
+ border-top: 1px solid var(--c-divider-2);
59
60
  border-radius: 0 0 6px 6px;
60
- padding-top: 8px;
61
61
  padding-right: var(--table-padding-right);
62
- padding-bottom: 8px;
63
62
  padding-left: var(--table-padding-left);
64
- background-color: var(--c-bg-elv-up);
63
+ background-color: var(--c-bg-elv-3);
65
64
 
66
65
  &.borderless {
67
66
  border-radius: 0;
@@ -71,11 +70,13 @@ const hasNext = computed(() => {
71
70
  .container {
72
71
  display: flex;
73
72
  justify-content: flex-end;
73
+ padding-right: 16px;
74
+ min-height: 47px;
74
75
  }
75
76
 
76
77
  .info {
77
- padding-right: 8px;
78
- padding-top: 4px;
78
+ margin: 0;
79
+ padding: 13px 0 10px;
79
80
  line-height: 24px;
80
81
  font-size: 12px;
81
82
  font-weight: 500;
@@ -84,7 +85,12 @@ const hasNext = computed(() => {
84
85
 
85
86
  .actions {
86
87
  display: flex;
87
- padding-right: 8px;
88
+ margin-right: -8px;
89
+ padding: 8px 0 7px;
90
+
91
+ .info + & {
92
+ padding-left: 8px;
93
+ }
88
94
  }
89
95
 
90
96
  .button {
@@ -104,7 +110,7 @@ const hasNext = computed(() => {
104
110
  }
105
111
 
106
112
  &.active:hover {
107
- background-color: var(--c-bg-elv-down);
113
+ background-color: var(--c-bg-elv-1);
108
114
  }
109
115
  }
110
116
 
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { format } from '../support/Num'
3
+ import { isNullish } from '../support/Utils'
3
4
 
4
5
  defineProps<{
5
6
  total?: number | null
@@ -12,8 +13,8 @@ defineProps<{
12
13
  <template>
13
14
  <div class="STableHeader" :class="{ borderless }">
14
15
  <div class="container">
15
- <p class="total">
16
- {{ format(total ?? 0) }} {{ (total ?? 0) > 1 ? 'records' : 'record' }}
16
+ <p v-if="!isNullish(total)" class="total">
17
+ {{ format(total) }} {{ (total) > 1 ? 'records' : 'record' }}
17
18
  </p>
18
19
 
19
20
  <div v-if="reset" class="reset">
@@ -34,9 +35,11 @@ defineProps<{
34
35
  .container {
35
36
  display: flex;
36
37
  padding: 0 16px;
38
+ min-height: 48px;
37
39
  }
38
40
 
39
41
  .total {
42
+ margin: 0;
40
43
  padding: 13px 0 11px;
41
44
  line-height: 24px;
42
45
  font-size: 12px;
@@ -46,14 +49,17 @@ defineProps<{
46
49
 
47
50
  .reset {
48
51
  position: relative;
49
- margin-left: 16px;
50
52
 
51
- &::before {
53
+ .total + & {
54
+ margin-left: 16px;
55
+ }
56
+
57
+ .total + &::before {
52
58
  display: inline-block;
53
59
  margin-right: 16px;
54
60
  width: 1px;
55
61
  height: 16px;
56
- background-color: var(--c-divider-light);
62
+ background-color: var(--c-divider-2);
57
63
  content: "";
58
64
  transform: translateY(4px);
59
65
  }
@@ -64,11 +70,11 @@ defineProps<{
64
70
  line-height: 24px;
65
71
  font-size: 12px;
66
72
  font-weight: 500;
67
- color: var(--c-info);
73
+ color: var(--c-info-text);
68
74
  transition: color 0.25s;
69
75
 
70
76
  &:hover {
71
- color: var(--c-info-dark);
77
+ color: var(--c-info-text-dark);
72
78
  }
73
79
  }
74
80
  </style>