@globalbrain/sefirot 4.31.0 → 4.32.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 (131) hide show
  1. package/client.d.ts +5 -0
  2. package/config/nuxt.d.ts +1 -0
  3. package/config/nuxt.js +1 -1
  4. package/config/vite.js +3 -2
  5. package/lib/components/SActionList.vue +2 -2
  6. package/lib/components/SActionMenu.vue +4 -4
  7. package/lib/components/SAvatar.vue +1 -1
  8. package/lib/components/SAvatarStack.vue +12 -12
  9. package/lib/components/SButton.vue +5 -7
  10. package/lib/components/SCard.vue +2 -2
  11. package/lib/components/SChartBar.vue +37 -12
  12. package/lib/components/SChartPie.vue +13 -7
  13. package/lib/components/SControlActionBarCollapse.vue +2 -4
  14. package/lib/components/SControlInputSearch.vue +7 -2
  15. package/lib/components/SDataListItem.vue +1 -3
  16. package/lib/components/SDescAvatar.vue +1 -1
  17. package/lib/components/SDescDay.vue +2 -2
  18. package/lib/components/SDescFile.vue +2 -4
  19. package/lib/components/SDescItem.vue +2 -1
  20. package/lib/components/SDescLabel.vue +1 -1
  21. package/lib/components/SDescLink.vue +2 -7
  22. package/lib/components/SDescPill.vue +2 -4
  23. package/lib/components/SDescText.vue +2 -2
  24. package/lib/components/SDropdown.vue +1 -1
  25. package/lib/components/SDropdownSectionFilter.vue +11 -11
  26. package/lib/components/SFragment.vue +1 -1
  27. package/lib/components/SInputAddon.vue +13 -11
  28. package/lib/components/SInputBase.vue +11 -11
  29. package/lib/components/SInputCheckbox.vue +6 -3
  30. package/lib/components/SInputCheckboxes.vue +7 -3
  31. package/lib/components/SInputDate.vue +3 -5
  32. package/lib/components/SInputDropdown.vue +26 -28
  33. package/lib/components/SInputDropdownItem.vue +21 -11
  34. package/lib/components/SInputFile.vue +9 -6
  35. package/lib/components/SInputFileUpload.vue +9 -11
  36. package/lib/components/SInputFileUploadItem.vue +8 -4
  37. package/lib/components/SInputHMS.vue +3 -1
  38. package/lib/components/SInputImage.vue +4 -2
  39. package/lib/components/SInputNumber.vue +9 -10
  40. package/lib/components/SInputRadio.vue +3 -2
  41. package/lib/components/SInputRadios.vue +6 -5
  42. package/lib/components/SInputSegments.vue +5 -7
  43. package/lib/components/SInputSegmentsOption.vue +1 -2
  44. package/lib/components/SInputSelect.vue +6 -4
  45. package/lib/components/SInputSwitch.vue +4 -1
  46. package/lib/components/SInputSwitches.vue +5 -3
  47. package/lib/components/SInputText.vue +16 -16
  48. package/lib/components/SInputTextarea.vue +7 -3
  49. package/lib/components/SInputYMD.vue +3 -1
  50. package/lib/components/SLink.vue +2 -2
  51. package/lib/components/SLocalNav.vue +1 -1
  52. package/lib/components/SLocalNavActions.vue +1 -1
  53. package/lib/components/SLocalNavMenu.vue +2 -2
  54. package/lib/components/SLoginPagePasswordDialog.vue +2 -2
  55. package/lib/components/SM.vue +1 -1
  56. package/lib/components/SMFade.vue +1 -1
  57. package/lib/components/SMarkdown.vue +3 -2
  58. package/lib/components/SModal.vue +2 -2
  59. package/lib/components/SSnackbar.vue +2 -2
  60. package/lib/components/SSteps.vue +6 -6
  61. package/lib/components/STable.vue +70 -27
  62. package/lib/components/STableCell.vue +14 -17
  63. package/lib/components/STableCellAvatars.vue +1 -1
  64. package/lib/components/STableCellDay.vue +12 -5
  65. package/lib/components/STableCellNumber.vue +2 -3
  66. package/lib/components/STableCellPath.vue +2 -2
  67. package/lib/components/STableCellText.vue +2 -3
  68. package/lib/components/STableColumn.vue +38 -16
  69. package/lib/components/STableFooter.vue +10 -2
  70. package/lib/components/STableHeader.vue +0 -1
  71. package/lib/components/STableHeaderMenu.vue +4 -4
  72. package/lib/components/STableHeaderMenuItem.vue +1 -1
  73. package/lib/components/STooltip.vue +3 -3
  74. package/lib/composables/Dropdown.ts +10 -1
  75. package/lib/composables/Error.ts +37 -37
  76. package/lib/composables/Grid.ts +1 -4
  77. package/lib/composables/Image.ts +2 -3
  78. package/lib/composables/Lang.ts +1 -1
  79. package/lib/composables/Markdown.ts +1 -1
  80. package/lib/composables/Table.ts +0 -2
  81. package/lib/composables/Theme.ts +1 -1
  82. package/lib/composables/Utils.ts +11 -4
  83. package/lib/composables/Validation.ts +1 -4
  84. package/lib/http/Http.ts +23 -17
  85. package/lib/styles/variables-deprecated.css +0 -1
  86. package/lib/styles/variables.css +16 -16
  87. package/lib/support/Chart.ts +0 -1
  88. package/lib/support/DateRange.ts +1 -1
  89. package/lib/support/Day.ts +12 -65
  90. package/lib/support/File.ts +7 -16
  91. package/lib/support/Utils.ts +7 -40
  92. package/lib/validation/Rule.ts +6 -21
  93. package/lib/validation/rules/decimal.ts +3 -3
  94. package/lib/validation/rules/email.ts +1 -1
  95. package/lib/validation/rules/index.ts +1 -1
  96. package/lib/validation/rules/negativeInteger.ts +1 -1
  97. package/lib/validation/rules/positiveInteger.ts +1 -1
  98. package/lib/validation/rules/requiredHms.ts +2 -2
  99. package/lib/validation/rules/requiredIf.ts +1 -4
  100. package/lib/validation/rules/requiredYmd.ts +2 -2
  101. package/lib/validation/rules/slackChannelName.ts +4 -1
  102. package/lib/validation/rules/zeroOrNegativeInteger.ts +1 -1
  103. package/lib/validation/rules/zeroOrPositiveInteger.ts +1 -1
  104. package/lib/validation/validators/after.ts +1 -5
  105. package/lib/validation/validators/afterOrEqual.ts +1 -5
  106. package/lib/validation/validators/before.ts +1 -4
  107. package/lib/validation/validators/beforeOrEqual.ts +1 -5
  108. package/lib/validation/validators/decimal.ts +7 -9
  109. package/lib/validation/validators/email.ts +4 -8
  110. package/lib/validation/validators/fileExtension.ts +7 -15
  111. package/lib/validation/validators/hms.ts +26 -15
  112. package/lib/validation/validators/index.ts +0 -2
  113. package/lib/validation/validators/maxFileSize.ts +3 -13
  114. package/lib/validation/validators/maxLength.ts +1 -7
  115. package/lib/validation/validators/maxTotalFileSize.ts +3 -26
  116. package/lib/validation/validators/maxValue.ts +2 -6
  117. package/lib/validation/validators/minLength.ts +1 -7
  118. package/lib/validation/validators/minValue.ts +2 -6
  119. package/lib/validation/validators/month.ts +1 -7
  120. package/lib/validation/validators/negativeInteger.ts +1 -7
  121. package/lib/validation/validators/positiveInteger.ts +1 -7
  122. package/lib/validation/validators/required.ts +5 -28
  123. package/lib/validation/validators/requiredHmsIf.ts +11 -13
  124. package/lib/validation/validators/requiredIf.ts +5 -11
  125. package/lib/validation/validators/requiredYmdIf.ts +11 -13
  126. package/lib/validation/validators/slackChannelName.ts +11 -11
  127. package/lib/validation/validators/url.ts +7 -5
  128. package/lib/validation/validators/ymd.ts +36 -30
  129. package/package.json +38 -38
  130. package/lib/validation/validators/requiredHms.ts +0 -9
  131. package/lib/validation/validators/requiredYmd.ts +0 -9
@@ -36,7 +36,9 @@ const emit = defineEmits<{
36
36
  const _value = computed(() => {
37
37
  return props.modelValue !== undefined
38
38
  ? props.modelValue
39
- : props.value !== undefined ? props.value : null
39
+ : props.value !== undefined
40
+ ? props.value
41
+ : null
40
42
  })
41
43
 
42
44
  const isFocused = ref(false)
@@ -103,11 +105,11 @@ function emitChange(e: any): void {
103
105
  </option>
104
106
 
105
107
  <option
106
- v-for="option, index in options"
108
+ v-for="(option, i) in options"
107
109
  :key="JSON.stringify(option)"
108
110
  :style="{ display: option.disabled ? 'none' : undefined }"
109
111
  class="option"
110
- :value="index"
112
+ :value="i"
111
113
  :selected="isSelectedOption(option)"
112
114
  >
113
115
  {{ option.label }}
@@ -214,7 +216,7 @@ function emitChange(e: any): void {
214
216
  }
215
217
 
216
218
  .option {
217
- color: initial;
219
+ background-color: var(--input-bg-color);
218
220
  }
219
221
 
220
222
  .icon {
@@ -36,7 +36,9 @@ const emit = defineEmits<{
36
36
  const _value = computed(() => {
37
37
  return props.modelValue !== undefined
38
38
  ? props.modelValue
39
- : props.value !== undefined ? props.value : false
39
+ : props.value !== undefined
40
+ ? props.value
41
+ : false
40
42
  })
41
43
 
42
44
  const classes = computed(() => [
@@ -67,6 +69,7 @@ function emitChange(): void {
67
69
  :check-color
68
70
  :help
69
71
  :hide-error
72
+ :validation
70
73
  >
71
74
  <div class="container">
72
75
  <div class="input" :class="{ on: _value }" role="button" @click="emitChange">
@@ -40,7 +40,7 @@ function isChecked(value: any): boolean {
40
40
  return props.modelValue.includes(value)
41
41
  }
42
42
 
43
- function handleChange(value: any): void {
43
+ function onChange(value: any): void {
44
44
  const difference = props.modelValue
45
45
  .filter((v) => v !== value)
46
46
  .concat(props.modelValue.includes(value) ? [] : [value])
@@ -63,15 +63,17 @@ function handleChange(value: any): void {
63
63
  :check-text
64
64
  :check-color
65
65
  :hide-error
66
+ :validation
66
67
  >
67
68
  <div class="container">
68
69
  <div class="row">
69
- <div v-for="(option, index) in options" :key="index" class="col">
70
+ <div v-for="(option, i) in options" :key="i" class="col">
70
71
  <SInputSwitch
71
72
  size="sm"
72
73
  :text="option.label"
74
+ :disabled
73
75
  :model-value="isChecked(option.value)"
74
- @update:model-value="handleChange(option.value)"
76
+ @update:model-value="onChange(option.value)"
75
77
  />
76
78
  </div>
77
79
  </div>
@@ -1,6 +1,5 @@
1
1
  <script setup lang="ts">
2
2
  import { type Component, computed, ref } from 'vue'
3
- import { isString } from '../support/Utils'
4
3
  import SInputBase, { type Props as BaseProps } from './SInputBase.vue'
5
4
 
6
5
  export type { Color, Size } from './SInputBase.vue'
@@ -37,17 +36,12 @@ const classes = computed(() => [
37
36
  { disabled: props.disabled }
38
37
  ])
39
38
 
40
- const inputClasses = computed(() => [
41
- textColor.value,
42
- { hide: showDisplay.value }
43
- ])
44
-
45
39
  const textColor = computed(() => {
46
40
  if (!props.textColor) {
47
41
  return 'neutral'
48
42
  }
49
43
 
50
- if (isString(props.textColor)) {
44
+ if (typeof props.textColor === 'string') {
51
45
  return props.textColor
52
46
  }
53
47
 
@@ -58,6 +52,11 @@ const showDisplay = computed(() => {
58
52
  return !isFocused.value && props.displayValue
59
53
  })
60
54
 
55
+ const inputClasses = computed(() => [
56
+ textColor.value,
57
+ { hide: showDisplay.value }
58
+ ])
59
+
61
60
  function focus(): void {
62
61
  input.value?.focus()
63
62
  }
@@ -78,9 +77,10 @@ function emitBlur(e: FocusEvent): void {
78
77
  }
79
78
 
80
79
  function emitInput(e: Event): void {
81
- const v = getValue(e)
82
- emit('update:model-value', v)
83
- emit('input', v)
80
+ const value = getValue(e)
81
+
82
+ emit('update:model-value', value)
83
+ emit('input', value)
84
84
  }
85
85
 
86
86
  function emitEnter(e: KeyboardEvent): void {
@@ -124,21 +124,21 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
124
124
 
125
125
  <div class="value">
126
126
  <div v-if="unitBefore" class="unit before">
127
- <span v-if="isString(unitBefore)" class="unit-text">{{ unitBefore }}</span>
128
- <component v-else :is="unitBefore" class="unit-icon" />
127
+ <span v-if="typeof unitBefore === 'string'" class="unit-text">{{ unitBefore }}</span>
128
+ <component :is="unitBefore" v-else class="unit-icon" />
129
129
  </div>
130
130
 
131
131
  <div class="area">
132
132
  <input
133
+ :id="name"
134
+ ref="input"
133
135
  class="input entity"
134
136
  :class="inputClasses"
135
- :id="name"
136
137
  :type="type ?? 'text'"
137
138
  :placeholder
138
139
  :disabled
139
140
  :tabindex
140
141
  :value="modelValue"
141
- ref="input"
142
142
  @focus="onFocus"
143
143
  @blur="emitBlur"
144
144
  @input="emitInput"
@@ -150,8 +150,8 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
150
150
  </div>
151
151
 
152
152
  <div v-if="unitAfter" class="unit after">
153
- <span v-if="isString(unitAfter)" class="unit-text">{{ unitAfter }}</span>
154
- <component v-else :is="unitAfter" class="unit-icon" />
153
+ <span v-if="typeof unitAfter === 'string'" class="unit-text">{{ unitAfter }}</span>
154
+ <component :is="unitAfter" v-else class="unit-icon" />
155
155
  </div>
156
156
  </div>
157
157
 
@@ -39,7 +39,9 @@ const sizePaddingYDict = {
39
39
  const _value = computed(() => {
40
40
  return props.modelValue !== undefined
41
41
  ? props.modelValue
42
- : props.value !== undefined ? props.value : null
42
+ : props.value !== undefined
43
+ ? props.value
44
+ : null
43
45
  })
44
46
 
45
47
  const classes = computed(() => [
@@ -58,7 +60,9 @@ const style = computed(() => {
58
60
  const fontSize = 24
59
61
 
60
62
  const minHeight = `min-height: ${rows * fontSize + padding}px;`
61
- const maxHeight = props.autoResize === true ? '' : `max-height: calc(${props.autoResize}lh + ${padding}px);`
63
+ const maxHeight = props.autoResize === true
64
+ ? ''
65
+ : `max-height: calc(${props.autoResize}lh + ${padding}px);`
62
66
 
63
67
  return `field-sizing:content;${minHeight}${maxHeight}`
64
68
  })
@@ -101,11 +105,11 @@ const isPreview = ref(false)
101
105
  <div class="box">
102
106
  <div v-if="preview !== undefined" class="control">
103
107
  <SInputSegments
108
+ v-model="isPreview"
104
109
  :options="[
105
110
  { label: writeLabel ?? 'Write', value: false },
106
111
  { label: previewLabel ?? 'Preview', value: true }
107
112
  ]"
108
- v-model="isPreview"
109
113
  size="mini"
110
114
  />
111
115
  </div>
@@ -38,7 +38,9 @@ const emit = defineEmits<{
38
38
  const _value = computed(() => {
39
39
  return props.modelValue !== undefined
40
40
  ? props.modelValue
41
- : props.value !== undefined ? props.value : null
41
+ : props.value !== undefined
42
+ ? props.value
43
+ : null
42
44
  })
43
45
 
44
46
  const padValue = computed(() => {
@@ -30,8 +30,8 @@ const component = computed(() => {
30
30
  return isExternal.value ? 'a' : 'router-link'
31
31
  })
32
32
 
33
- const target = computed(() => isExternal.value ? '_blank' : null)
34
- const rel = computed(() => isExternal.value ? 'noopener noreferrer' : null)
33
+ const target = computed(() => (isExternal.value ? '_blank' : null))
34
+ const rel = computed(() => (isExternal.value ? 'noopener noreferrer' : null))
35
35
  </script>
36
36
 
37
37
  <template>
@@ -6,7 +6,7 @@ import SLocalNavDescription from './SLocalNavDescription.vue'
6
6
  import SLocalNavMenu, { type MenuItem } from './SLocalNavMenu.vue'
7
7
  import SLocalNavTitle, { type Title } from './SLocalNavTitle.vue'
8
8
 
9
- export type { Title, Action, MenuItem }
9
+ export type { Action, MenuItem, Title }
10
10
 
11
11
  export interface Avatar {
12
12
  image?: string | null
@@ -26,7 +26,7 @@ defineProps<{
26
26
 
27
27
  <template>
28
28
  <div class="SLocalNavActions">
29
- <div v-for="action, i in actions" :key="i" class="action">
29
+ <div v-for="(action, i) in actions" :key="i" class="action">
30
30
  <SButton
31
31
  size="small"
32
32
  :tag="action.tag"
@@ -16,10 +16,10 @@ defineProps<{
16
16
 
17
17
  <template>
18
18
  <div class="SLocalNavMenu">
19
- <div v-for="nav, index in menu" :key="index" class="group">
19
+ <div v-for="(nav, i) in menu" :key="i" class="group">
20
20
  <div v-for="item in nav" :key="item.text" class="item">
21
21
  <SLink class="link" :class="{ active: item.active }" :href="item.link">
22
- <component v-if="item.icon" :is="item.icon" class="icon-svg" />
22
+ <component :is="item.icon" v-if="item.icon" class="icon-svg" />
23
23
  <span class="text">{{ item.text }}</span>
24
24
  </SLink>
25
25
  </div>
@@ -57,19 +57,19 @@ async function onSubmit() {
57
57
  <p>Invalid email or password.</p>
58
58
  </SAlert>
59
59
  <SInputText
60
+ v-model="data.email"
60
61
  name="email"
61
62
  type="email"
62
63
  label="Email"
63
64
  placeholder="john.doe@example.com"
64
- v-model="data.email"
65
65
  :validation="validation.email"
66
66
  />
67
67
  <SInputText
68
+ v-model="data.password"
68
69
  name="password"
69
70
  type="password"
70
71
  label="Password"
71
72
  placeholder="Password"
72
- v-model="data.password"
73
73
  :validation="validation.password"
74
74
  />
75
75
  </SDoc>
@@ -34,7 +34,7 @@ const { stop } = useIntersectionObserver(target, ([{ isIntersecting }]) => {
34
34
  </script>
35
35
 
36
36
  <template>
37
- <component :is="as" class="SM" :class="{ on }" ref="target">
37
+ <component :is="as" ref="target" class="SM" :class="{ on }">
38
38
  <slot :on />
39
39
  </component>
40
40
  </template>
@@ -8,7 +8,7 @@ withDefaults(defineProps<Props>(), {
8
8
  </script>
9
9
 
10
10
  <template>
11
- <SM class="SMFade" v-bind="$props" v-slot="{ on }">
11
+ <SM v-slot="{ on }" class="SMFade" v-bind="$props">
12
12
  <slot :on />
13
13
  </SM>
14
14
  </template>
@@ -17,8 +17,9 @@ const rendered = computed(() => markdown(props.content))
17
17
  </script>
18
18
 
19
19
  <template>
20
- <component v-if="$slots.default" :is="tag" class="SMarkdown-container">
20
+ <component :is="tag" v-if="$slots.default" class="SMarkdown-container">
21
21
  <slot v-bind="{ rendered }" />
22
22
  </component>
23
- <component v-else :is="tag" class="SMarkdown-container" v-html="rendered" />
23
+ <!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
24
+ <component :is="tag" v-else class="SMarkdown-container" v-html="rendered" />
24
25
  </template>
@@ -30,7 +30,7 @@ function onClick(e: MouseEvent) {
30
30
  <template>
31
31
  <Teleport to="#sefirot-modals">
32
32
  <Transition name="fade">
33
- <div v-if="open" class="SModal" ref="el" @mousedown="onClick">
33
+ <div v-if="open" ref="el" class="SModal" @mousedown="onClick">
34
34
  <slot />
35
35
  </div>
36
36
  </Transition>
@@ -45,7 +45,7 @@ function onClick(e: MouseEvent) {
45
45
  bottom: 0;
46
46
  left: 0;
47
47
  z-index: var(--z-index-backdrop);
48
- background-color: rgba(0, 0, 0, .8);
48
+ background-color: rgba(0, 0, 0, 0.8);
49
49
  transition: opacity 0.25s;
50
50
  overflow: hidden;
51
51
  overflow-y: auto;
@@ -28,7 +28,7 @@ function close() {
28
28
  </p>
29
29
 
30
30
  <div v-if="actions" class="actions">
31
- <div v-for="(action, index) in actions" :key="index" class="action">
31
+ <div v-for="(action, i) in actions" :key="i" class="action">
32
32
  <SButton
33
33
  size="medium"
34
34
  type="text"
@@ -87,7 +87,7 @@ function close() {
87
87
  width: 16px;
88
88
  height: 16px;
89
89
  color: var(--c-text-3);
90
- transition: color .25s;
90
+ transition: color 0.25s;
91
91
  }
92
92
 
93
93
  .content {
@@ -19,7 +19,7 @@ function getBarLeftMode(index: number): BarMode | null {
19
19
  const current = props.steps[index]
20
20
  const prev = props.steps[index - 1]
21
21
 
22
- return (isActive(prev) && isActive(current)) ? 'active' : 'mute'
22
+ return isActive(prev) && isActive(current) ? 'active' : 'mute'
23
23
  }
24
24
 
25
25
  function getBarRightMode(index: number): BarMode | null {
@@ -30,7 +30,7 @@ function getBarRightMode(index: number): BarMode | null {
30
30
  const current = props.steps[index]
31
31
  const next = props.steps[index + 1]
32
32
 
33
- return (isActive(current) && isActive(next)) ? 'active' : 'mute'
33
+ return isActive(current) && isActive(next) ? 'active' : 'mute'
34
34
  }
35
35
 
36
36
  function isActive(step: Step): boolean {
@@ -41,12 +41,12 @@ function isActive(step: Step): boolean {
41
41
  <template>
42
42
  <div class="SSteps">
43
43
  <SStep
44
- v-for="(step, index) in steps"
45
- :key="index"
44
+ v-for="(step, i) in steps"
45
+ :key="i"
46
46
  class="item"
47
47
  :style="{ width }"
48
- :bar-left="getBarLeftMode(index)"
49
- :bar-right="getBarRightMode(index)"
48
+ :bar-left="getBarLeftMode(i)"
49
+ :bar-right="getBarRightMode(i)"
50
50
  v-bind="step"
51
51
  />
52
52
  </div>
@@ -22,6 +22,7 @@ const head = shallowRef<HTMLElement | null>(null)
22
22
  const body = shallowRef<HTMLElement | null>(null)
23
23
  const block = shallowRef<HTMLElement | null>(null)
24
24
  const row = shallowRef<HTMLElement | null>(null)
25
+ const table = shallowRef<HTMLElement | null>(null)
25
26
 
26
27
  const ordersToShow = smartComputed(() => {
27
28
  const orders = unref(props.options.orders).filter((key) => {
@@ -36,7 +37,7 @@ const ordersToShow = smartComputed(() => {
36
37
  return ['__select', ...orders]
37
38
  })
38
39
 
39
- watch(() => ordersToShow.value, handleResize)
40
+ watch(() => ordersToShow.value, onResize)
40
41
 
41
42
  const colToGrowAdjusted = ref(false)
42
43
 
@@ -61,6 +62,12 @@ const cellOfColToGrow = computed(() => {
61
62
  const colWidths = reactive<Record<string, string>>({})
62
63
  const blockWidth = ref<number | undefined>()
63
64
 
65
+ const resizeState = ref<{
66
+ columnName: string
67
+ startX: number
68
+ indicatorX: number
69
+ } | null>(null)
70
+
64
71
  watch(() => unref(props.options.columns), (columns) => {
65
72
  Object.keys(columns).forEach((key) => {
66
73
  const width = columns[key]?.width
@@ -77,7 +84,6 @@ const showHeader = computed(() => {
77
84
 
78
85
  return (
79
86
  unref(props.options.total) != null
80
- || !!unref(props.options.reset)
81
87
  || !!unref(props.options.menu)
82
88
  )
83
89
  })
@@ -201,7 +207,6 @@ const frozenColumns = smartComputed(() => {
201
207
  })
202
208
 
203
209
  const frozenColWidths = smartComputed(() => {
204
- // eslint-disable-next-line no-void
205
210
  void blockWidth.value
206
211
  return frozenColumns.value.map((key) => getColWidth(key))
207
212
  })
@@ -210,7 +215,7 @@ useResizeObserver(block, ([entry]) => {
210
215
  blockWidth.value = entry.contentRect.width
211
216
  })
212
217
 
213
- const resizeObserver = useResizeObserver(head, handleResize)
218
+ const resizeObserver = useResizeObserver(head, onResize)
214
219
 
215
220
  const font = typeof document !== 'undefined'
216
221
  ? `500 12px ${getComputedStyle(document.body).fontFamily}`
@@ -267,7 +272,7 @@ function stopObserving() {
267
272
  resizeObserver.stop()
268
273
  }
269
274
 
270
- async function handleResize() {
275
+ async function onResize() {
271
276
  if (colToGrow.value < 0 || !cellOfColToGrow.value || !row.value) {
272
277
  stopObserving()
273
278
  return
@@ -387,6 +392,29 @@ function getStyles(key: string) {
387
392
  '--table-col-left': widthSum ? `calc(${widthSum})` : '0px'
388
393
  }
389
394
  }
395
+
396
+ function onResizeStart(data: { columnName: string; startX: number; initialX: number }) {
397
+ const tableRect = table.value?.getBoundingClientRect()
398
+ const tableLeft = tableRect?.left ?? 0
399
+ const indicatorX = data.initialX - tableLeft
400
+
401
+ resizeState.value = {
402
+ columnName: data.columnName,
403
+ startX: indicatorX,
404
+ indicatorX
405
+ }
406
+ }
407
+
408
+ function onResizeMove(data: { deltaX: number }) {
409
+ if (resizeState.value) {
410
+ resizeState.value.indicatorX = resizeState.value.startX + data.deltaX
411
+ }
412
+ }
413
+
414
+ function onResizeEnd(data: { columnName: string; finalWidth: string }) {
415
+ updateColWidth(data.columnName, data.finalWidth, true)
416
+ resizeState.value = null
417
+ }
390
418
  </script>
391
419
 
392
420
  <template>
@@ -395,17 +423,21 @@ function getStyles(key: string) {
395
423
  <STableHeader
396
424
  v-if="showHeader"
397
425
  :total="unref(options.total)"
398
- :reset="unref(options.reset)"
399
426
  :menu="unref(options.menu)"
400
427
  :borderless="unref(options.borderless)"
401
- :on-reset="options.onReset"
402
428
  :selected="Array.isArray(selected) ? selected : undefined"
403
429
  />
404
430
 
405
- <div class="table" role="grid">
406
- <div class="container head" ref="head" @scroll="syncHeadScroll">
407
- <div class="block" ref="block">
408
- <div class="row" ref="row">
431
+ <div ref="table" class="table" role="grid">
432
+ <div
433
+ v-if="resizeState"
434
+ class="resize-indicator"
435
+ :style="{ left: `${resizeState.indicatorX}px` }"
436
+ />
437
+
438
+ <div ref="head" class="container head" @scroll="syncHeadScroll">
439
+ <div ref="block" class="block">
440
+ <div ref="row" class="row">
409
441
  <STableItem
410
442
  v-for="key in ordersToShow"
411
443
  :key
@@ -421,7 +453,9 @@ function getStyles(key: string) {
421
453
  :dropdown="unref(options.columns)[key]?.dropdown"
422
454
  :has-header="showHeader"
423
455
  :resizable="unref(options.columns)[key]?.resizable"
424
- @resize="(value) => updateColWidth(key, value, true)"
456
+ @resize-start="onResizeStart"
457
+ @resize-move="onResizeMove"
458
+ @resize-end="onResizeEnd"
425
459
  >
426
460
  <SInputCheckbox
427
461
  v-if="
@@ -440,8 +474,8 @@ function getStyles(key: string) {
440
474
 
441
475
  <div
442
476
  v-if="!unref(options.loading) && unref(options.records)?.length"
443
- class="container body"
444
477
  ref="body"
478
+ class="container body"
445
479
  @scroll="syncBodyScroll"
446
480
  >
447
481
  <div
@@ -452,10 +486,10 @@ function getStyles(key: string) {
452
486
  position: 'relative'
453
487
  }"
454
488
  >
455
- <div v-for="{ index, key: __key, size, start } in virtualItems" :key="__key">
489
+ <div v-for="{ index: i, key: __key, size, start } in virtualItems" :key="__key">
456
490
  <div
457
491
  class="row"
458
- :class="isSummaryOrLastClass(index)"
492
+ :class="isSummaryOrLastClass(i)"
459
493
  :style="{
460
494
  position: 'absolute',
461
495
  top: 0,
@@ -475,25 +509,24 @@ function getStyles(key: string) {
475
509
  >
476
510
  <STableCell
477
511
  :name="key"
478
- :class="isSummary(index) && 'summary'"
512
+ :class="isSummary(i) && 'summary'"
479
513
  :class-name="unref(options.columns)[key]?.className"
480
- :cell="getCell(key, index)"
481
- :value="recordsWithSummary[index][key]"
482
- :record="recordsWithSummary[index]"
483
- :records="unref(options.records)!"
514
+ :cell="getCell(key, i)"
515
+ :value="recordsWithSummary[i][key]"
516
+ :record="recordsWithSummary[i]"
484
517
  >
485
- <template v-if="key === '__select' && !isSummary(index)">
518
+ <template v-if="key === '__select' && !isSummary(i)">
486
519
  <SInputCheckbox
487
520
  v-if="Array.isArray(selected)"
488
- :model-value="selected.includes(indexes[index])"
489
- @update:model-value="(c) => (c ? addSelected : removeSelected)(indexes[index])"
490
- :disabled="options.disableSelection?.(recordsWithSummary[index]) === true"
521
+ :model-value="selected.includes(indexes[i])"
522
+ :disabled="options.disableSelection?.(recordsWithSummary[i]) === true"
523
+ @update:model-value="(c) => (c ? addSelected : removeSelected)(indexes[i])"
491
524
  />
492
525
  <SInputRadio
493
526
  v-else
494
- :model-value="selected === indexes[index]"
495
- @update:model-value="(c) => updateSelected(c ? indexes[index] : null)"
496
- :disabled="options.disableSelection?.(recordsWithSummary[index]) === true"
527
+ :model-value="selected === indexes[i]"
528
+ :disabled="options.disableSelection?.(recordsWithSummary[i]) === true"
529
+ @update:model-value="(c) => updateSelected(c ? indexes[i] : null)"
497
530
  />
498
531
  </template>
499
532
  </STableCell>
@@ -637,6 +670,16 @@ function getStyles(key: string) {
637
670
  color: var(--c-text-1);
638
671
  }
639
672
 
673
+ .resize-indicator {
674
+ position: absolute;
675
+ top: 0;
676
+ bottom: 0;
677
+ width: 1px;
678
+ background-color: var(--c-border-info-1);
679
+ z-index: 200;
680
+ pointer-events: none;
681
+ }
682
+
640
683
  .STable .col-__select {
641
684
  --table-col-width: calc(48px + var(--table-padding-left));
642
685