@veltra/desktop 1.5.0 → 1.6.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.
@@ -47,8 +47,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
47
47
  ];
48
48
  });
49
49
  const ripple = computed(() => {
50
- if (props.disabled || props.loading) return false;
51
- if ((props.plain || props.text) && props.type) return bem.is(`ripple-${props.type}`);
50
+ if (props.disabled || props.loading || props.text) return false;
51
+ if (props.plain && props.type) return bem.is(`ripple-${props.type}`);
52
52
  return true;
53
53
  });
54
54
  const handleClick = (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.vue"],"sourcesContent":["<template>\n <button\n :class=\"classList\"\n type=\"button\"\n aria-label=\"button\"\n ref=\"buttonRef\"\n v-ripple=\"ripple\"\n @click=\"handleClick\"\n >\n <!-- 加载图标 -->\n <u-icon v-if=\"loading\" :class=\"[bem.is('loading')]\" :size=\"iconSize\">\n <component :is=\"loadingIcon\" />\n </u-icon>\n\n <!-- 左侧图标 -->\n <u-icon v-if=\"!!icon && iconPosition === 'left' && !loading\" :size=\"iconSize\">\n <component :is=\"icon\" />\n </u-icon>\n\n <slot />\n\n <!-- 右侧图标 -->\n <u-icon v-if=\"!!icon && iconPosition === 'right'\" :size=\"iconSize\">\n <component :is=\"icon\" />\n </u-icon>\n </button>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps } from '@veltra/compositions'\nimport { vRipple } from '@veltra/directives'\nimport { Loading } from '@veltra/icons'\nimport { bem } from '@veltra/utils'\nimport { computed, shallowRef } from 'vue'\n\nimport type { ButtonEmits, ButtonProps, _ButtonExposed, ComponentSize } from '../../types'\nimport { UIcon } from '../icon'\n\ndefineOptions({ name: 'UButton' })\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n iconPosition: 'left',\n loadingIcon: () => Loading,\n disabled: false,\n propagate: true\n})\n\nconst { size } = useFallbackProps([props], { size: 'default' as ComponentSize })\n\nconst emit = defineEmits<ButtonEmits>()\n\nconst cls = bem('button')\n\nconst classList = computed(() => {\n return [\n cls.b,\n cls.m(size.value),\n props.type && cls.m('color-' + props.type),\n bem.is('circle', props.circle),\n bem.is('disabled', props.disabled),\n bem.is('loading', props.loading),\n bem.is('plain', props.plain),\n bem.is('text', props.text)\n ]\n})\n\nconst ripple = computed(() => {\n if (props.disabled || props.loading) return false\n if ((props.plain || props.text) && props.type) {\n return bem.is(`ripple-${props.type}`)\n }\n return true\n})\n\nconst handleClick = (e: MouseEvent) => {\n if (props.disabled || props.loading) {\n e.stopPropagation()\n return\n }\n\n !props.propagate && e.stopPropagation()\n\n emit('click', e)\n}\n\nconst buttonRef = shallowRef<HTMLButtonElement>()\n\nconst exposed: _ButtonExposed = { el: buttonRef }\n\ndefineExpose(exposed)\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwCA,MAAM,QAAQ;EAOd,MAAM,EAAE,SAAS,iBAAiB,CAAC,KAAK,GAAG,EAAE,MAAM,UAA2B,CAAC;EAE/E,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,QAAQ;EAExB,MAAM,YAAY,eAAe;GAC/B,OAAO;IACL,IAAI;IACJ,IAAI,EAAE,KAAK,KAAK;IAChB,MAAM,QAAQ,IAAI,EAAE,WAAW,MAAM,IAAI;IACzC,IAAI,GAAG,UAAU,MAAM,MAAM;IAC7B,IAAI,GAAG,YAAY,MAAM,QAAQ;IACjC,IAAI,GAAG,WAAW,MAAM,OAAO;IAC/B,IAAI,GAAG,SAAS,MAAM,KAAK;IAC3B,IAAI,GAAG,QAAQ,MAAM,IAAI;GAC3B;EACF,CAAC;EAED,MAAM,SAAS,eAAe;GAC5B,IAAI,MAAM,YAAY,MAAM,SAAS,OAAO;GAC5C,KAAK,MAAM,SAAS,MAAM,SAAS,MAAM,MACvC,OAAO,IAAI,GAAG,UAAU,MAAM,MAAM;GAEtC,OAAO;EACT,CAAC;EAED,MAAM,eAAe,MAAkB;GACrC,IAAI,MAAM,YAAY,MAAM,SAAS;IACnC,EAAE,gBAAgB;IAClB;GACF;GAEA,CAAC,MAAM,aAAa,EAAE,gBAAgB;GAEtC,KAAK,SAAS,CAAC;EACjB;EAEA,MAAM,YAAY,WAA8B;EAIhD,SAAa,EAFqB,IAAI,UAEzB,CAAO;;GAxFlB,OAAA,gBAAA,UAAA,GAAA,mBAwBS,UAAA;IAvBN,OAAK,eAAE,UAAA,KAAS;IACjB,MAAK;IACL,cAAW;IACP,SAAA;IAAJ,KAAI;IAEH,SAAO;;IAER,mBAAA,QAAA;IACc,QAAA,WAAA,UAAA,GAAd,YAES,MAAA,WAAA,GAAA;;KAFe,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,GAAE,SAAA,CAAA,CAAA;KAAe,MAAM,QAAA;;KAC1B,SAAA,cAAA,EAAA,UAAA,GAA/B,YAA+B,wBAAf,QAAA,WAAW,CAAA,EAAA,CAAA;;;IAG7B,mBAAA,QAAA;IACgB,CAAA,CAAA,QAAA,QAAQ,QAAA,iBAAY,UAAA,CAAgB,QAAA,WAAA,UAAA,GAApD,YAES,MAAA,WAAA,GAAA;;KAFqD,MAAM,QAAA;;KAC1C,SAAA,cAAA,EAAA,UAAA,GAAxB,YAAwB,wBAAR,QAAA,IAAI,CAAA,EAAA,CAAA;;;IAGtB,WAAQ,KAAA,QAAA,SAAA;IAER,mBAAA,QAAA;IACgB,CAAA,CAAA,QAAA,QAAQ,QAAA,iBAAY,WAAA,UAAA,GAApC,YAES,MAAA,WAAA,GAAA;;KAF0C,MAAM,QAAA;;KAC/B,SAAA,cAAA,EAAA,UAAA,GAAxB,YAAwB,wBAAR,QAAA,IAAI,CAAA,EAAA,CAAA;;;GAjBZ,GAAA,CAAA,IAAA,CAAA,CAAA,MAAA,OAAA,GAAA,OAAA,KAAM,CAAA,CAAA"}
1
+ {"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.vue"],"sourcesContent":["<template>\n <button\n :class=\"classList\"\n type=\"button\"\n aria-label=\"button\"\n ref=\"buttonRef\"\n v-ripple=\"ripple\"\n @click=\"handleClick\"\n >\n <!-- 加载图标 -->\n <u-icon v-if=\"loading\" :class=\"[bem.is('loading')]\" :size=\"iconSize\">\n <component :is=\"loadingIcon\" />\n </u-icon>\n\n <!-- 左侧图标 -->\n <u-icon v-if=\"!!icon && iconPosition === 'left' && !loading\" :size=\"iconSize\">\n <component :is=\"icon\" />\n </u-icon>\n\n <slot />\n\n <!-- 右侧图标 -->\n <u-icon v-if=\"!!icon && iconPosition === 'right'\" :size=\"iconSize\">\n <component :is=\"icon\" />\n </u-icon>\n </button>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps } from '@veltra/compositions'\nimport { vRipple } from '@veltra/directives'\nimport { Loading } from '@veltra/icons'\nimport { bem } from '@veltra/utils'\nimport { computed, shallowRef } from 'vue'\n\nimport type { ButtonEmits, ButtonProps, _ButtonExposed, ComponentSize } from '../../types'\nimport { UIcon } from '../icon'\n\ndefineOptions({ name: 'UButton' })\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n iconPosition: 'left',\n loadingIcon: () => Loading,\n disabled: false,\n propagate: true\n})\n\nconst { size } = useFallbackProps([props], { size: 'default' as ComponentSize })\n\nconst emit = defineEmits<ButtonEmits>()\n\nconst cls = bem('button')\n\nconst classList = computed(() => {\n return [\n cls.b,\n cls.m(size.value),\n props.type && cls.m('color-' + props.type),\n bem.is('circle', props.circle),\n bem.is('disabled', props.disabled),\n bem.is('loading', props.loading),\n bem.is('plain', props.plain),\n bem.is('text', props.text)\n ]\n})\n\nconst ripple = computed(() => {\n if (props.disabled || props.loading || props.text) return false\n if (props.plain && props.type) {\n return bem.is(`ripple-${props.type}`)\n }\n return true\n})\n\nconst handleClick = (e: MouseEvent) => {\n if (props.disabled || props.loading) {\n e.stopPropagation()\n return\n }\n\n !props.propagate && e.stopPropagation()\n\n emit('click', e)\n}\n\nconst buttonRef = shallowRef<HTMLButtonElement>()\n\nconst exposed: _ButtonExposed = { el: buttonRef }\n\ndefineExpose(exposed)\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwCA,MAAM,QAAQ;EAOd,MAAM,EAAE,SAAS,iBAAiB,CAAC,KAAK,GAAG,EAAE,MAAM,UAA2B,CAAC;EAE/E,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,QAAQ;EAExB,MAAM,YAAY,eAAe;GAC/B,OAAO;IACL,IAAI;IACJ,IAAI,EAAE,KAAK,KAAK;IAChB,MAAM,QAAQ,IAAI,EAAE,WAAW,MAAM,IAAI;IACzC,IAAI,GAAG,UAAU,MAAM,MAAM;IAC7B,IAAI,GAAG,YAAY,MAAM,QAAQ;IACjC,IAAI,GAAG,WAAW,MAAM,OAAO;IAC/B,IAAI,GAAG,SAAS,MAAM,KAAK;IAC3B,IAAI,GAAG,QAAQ,MAAM,IAAI;GAC3B;EACF,CAAC;EAED,MAAM,SAAS,eAAe;GAC5B,IAAI,MAAM,YAAY,MAAM,WAAW,MAAM,MAAM,OAAO;GAC1D,IAAI,MAAM,SAAS,MAAM,MACvB,OAAO,IAAI,GAAG,UAAU,MAAM,MAAM;GAEtC,OAAO;EACT,CAAC;EAED,MAAM,eAAe,MAAkB;GACrC,IAAI,MAAM,YAAY,MAAM,SAAS;IACnC,EAAE,gBAAgB;IAClB;GACF;GAEA,CAAC,MAAM,aAAa,EAAE,gBAAgB;GAEtC,KAAK,SAAS,CAAC;EACjB;EAEA,MAAM,YAAY,WAA8B;EAIhD,SAAa,EAFqB,IAAI,UAEzB,CAAO;;GAxFlB,OAAA,gBAAA,UAAA,GAAA,mBAwBS,UAAA;IAvBN,OAAK,eAAE,UAAA,KAAS;IACjB,MAAK;IACL,cAAW;IACP,SAAA;IAAJ,KAAI;IAEH,SAAO;;IAER,mBAAA,QAAA;IACc,QAAA,WAAA,UAAA,GAAd,YAES,MAAA,WAAA,GAAA;;KAFe,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,GAAE,SAAA,CAAA,CAAA;KAAe,MAAM,QAAA;;KAC1B,SAAA,cAAA,EAAA,UAAA,GAA/B,YAA+B,wBAAf,QAAA,WAAW,CAAA,EAAA,CAAA;;;IAG7B,mBAAA,QAAA;IACgB,CAAA,CAAA,QAAA,QAAQ,QAAA,iBAAY,UAAA,CAAgB,QAAA,WAAA,UAAA,GAApD,YAES,MAAA,WAAA,GAAA;;KAFqD,MAAM,QAAA;;KAC1C,SAAA,cAAA,EAAA,UAAA,GAAxB,YAAwB,wBAAR,QAAA,IAAI,CAAA,EAAA,CAAA;;;IAGtB,WAAQ,KAAA,QAAA,SAAA;IAER,mBAAA,QAAA;IACgB,CAAA,CAAA,QAAA,QAAQ,QAAA,iBAAY,WAAA,UAAA,GAApC,YAES,MAAA,WAAA,GAAA;;KAF0C,MAAM,QAAA;;KAC/B,SAAA,cAAA,EAAA,UAAA,GAAxB,YAAwB,wBAAR,QAAA,IAAI,CAAA,EAAA,CAAA;;;GAjBZ,GAAA,CAAA,IAAA,CAAA,CAAA,MAAA,OAAA,GAAA,OAAA,KAAM,CAAA,CAAA"}
@@ -51,16 +51,9 @@
51
51
  box-shadow: none !important;
52
52
  }
53
53
  .u-button.is-plain {
54
- background-color: var(--u-bg-color-top);
55
- border-color: var(--u-color-default-dark-1);
54
+ border-color: transparent;
56
55
  box-shadow: none;
57
56
  }
58
- .u-button.is-plain:hover {
59
- border-color: var(--u-color-default-dark-1);
60
- }
61
- .u-button.is-plain.is-disabled {
62
- color: var(--u-text-color-second);
63
- }
64
57
  .u-button--small {
65
58
  height: var(--u-form-component-height-small);
66
59
  line-height: var(--u-form-component-height-small);
@@ -133,37 +126,37 @@
133
126
  }
134
127
  .u-button--color-primary.is-text {
135
128
  color: var(--u-color-primary);
129
+ border-color: transparent;
136
130
  box-shadow: none;
137
131
  }
138
- .u-button--color-primary.is-text:hover {
139
- box-shadow: 0px 0px 0px 1px var(--u-color-primary);
140
- }
141
132
  .u-button--color-primary.is-text:not(.is-disabled):not(.is-loading):hover, .u-button--color-primary.is-text:not(.is-disabled):not(.is-loading):focus {
142
133
  background-color: var(--u-button-primary-plain-bg, var(--u-color-primary-light-9));
143
134
  }
144
135
  .u-button--color-primary.is-text.is-disabled {
136
+ border-color: transparent;
145
137
  color: var(--u-color-primary-light-5);
146
138
  }
147
139
  .u-button--color-primary.is-text.is-loading {
140
+ border-color: transparent;
148
141
  color: var(--u-color-primary-light-5);
149
142
  }
150
143
  .u-button--color-primary.is-plain {
144
+ border-color: transparent;
151
145
  color: var(--u-button-primary-plain-color, var(--u-color-primary));
152
146
  background-color: var(--u-button-primary-plain-bg, var(--u-color-primary-light-9));
153
- border-color: var(--u-button-primary-plain-border, var(--u-color-primary-light-5));
154
- box-shadow: none;
155
147
  }
156
148
  .u-button--color-primary.is-plain:not(.is-disabled):not(.is-loading):hover {
157
- color: var(--u-color-primary);
158
- border-color: var(--u-color-primary);
159
- background-color: var(--u-color-primary-light-9);
160
- }
161
- .u-button--color-primary.is-plain:active {
162
- box-shadow: none;
149
+ background-color: var(--u-color-primary-light-7);
163
150
  }
164
151
  .u-button--color-primary.is-plain.is-disabled {
152
+ border-color: transparent;
165
153
  color: var(--u-color-primary-light-5);
166
- border-color: var(--u-color-primary-light-8);
154
+ background-color: var(--u-color-primary-light-9);
155
+ }
156
+ .u-button--color-primary.is-plain.is-loading {
157
+ border-color: transparent;
158
+ color: var(--u-color-primary-light-5);
159
+ background-color: var(--u-color-primary-light-9);
167
160
  }
168
161
  .u-button .is-ripple-success {
169
162
  background-color: var(--u-color-success);
@@ -193,37 +186,37 @@
193
186
  }
194
187
  .u-button--color-success.is-text {
195
188
  color: var(--u-color-success);
189
+ border-color: transparent;
196
190
  box-shadow: none;
197
191
  }
198
- .u-button--color-success.is-text:hover {
199
- box-shadow: 0px 0px 0px 1px var(--u-color-success);
200
- }
201
192
  .u-button--color-success.is-text:not(.is-disabled):not(.is-loading):hover, .u-button--color-success.is-text:not(.is-disabled):not(.is-loading):focus {
202
193
  background-color: var(--u-button-success-plain-bg, var(--u-color-success-light-9));
203
194
  }
204
195
  .u-button--color-success.is-text.is-disabled {
196
+ border-color: transparent;
205
197
  color: var(--u-color-success-light-5);
206
198
  }
207
199
  .u-button--color-success.is-text.is-loading {
200
+ border-color: transparent;
208
201
  color: var(--u-color-success-light-5);
209
202
  }
210
203
  .u-button--color-success.is-plain {
204
+ border-color: transparent;
211
205
  color: var(--u-button-success-plain-color, var(--u-color-success));
212
206
  background-color: var(--u-button-success-plain-bg, var(--u-color-success-light-9));
213
- border-color: var(--u-button-success-plain-border, var(--u-color-success-light-5));
214
- box-shadow: none;
215
207
  }
216
208
  .u-button--color-success.is-plain:not(.is-disabled):not(.is-loading):hover {
217
- color: var(--u-color-success);
218
- border-color: var(--u-color-success);
219
- background-color: var(--u-color-success-light-9);
220
- }
221
- .u-button--color-success.is-plain:active {
222
- box-shadow: none;
209
+ background-color: var(--u-color-success-light-7);
223
210
  }
224
211
  .u-button--color-success.is-plain.is-disabled {
212
+ border-color: transparent;
225
213
  color: var(--u-color-success-light-5);
226
- border-color: var(--u-color-success-light-8);
214
+ background-color: var(--u-color-success-light-9);
215
+ }
216
+ .u-button--color-success.is-plain.is-loading {
217
+ border-color: transparent;
218
+ color: var(--u-color-success-light-5);
219
+ background-color: var(--u-color-success-light-9);
227
220
  }
228
221
  .u-button .is-ripple-warning {
229
222
  background-color: var(--u-color-warning);
@@ -253,37 +246,37 @@
253
246
  }
254
247
  .u-button--color-warning.is-text {
255
248
  color: var(--u-color-warning);
249
+ border-color: transparent;
256
250
  box-shadow: none;
257
251
  }
258
- .u-button--color-warning.is-text:hover {
259
- box-shadow: 0px 0px 0px 1px var(--u-color-warning);
260
- }
261
252
  .u-button--color-warning.is-text:not(.is-disabled):not(.is-loading):hover, .u-button--color-warning.is-text:not(.is-disabled):not(.is-loading):focus {
262
253
  background-color: var(--u-button-warning-plain-bg, var(--u-color-warning-light-9));
263
254
  }
264
255
  .u-button--color-warning.is-text.is-disabled {
256
+ border-color: transparent;
265
257
  color: var(--u-color-warning-light-5);
266
258
  }
267
259
  .u-button--color-warning.is-text.is-loading {
260
+ border-color: transparent;
268
261
  color: var(--u-color-warning-light-5);
269
262
  }
270
263
  .u-button--color-warning.is-plain {
264
+ border-color: transparent;
271
265
  color: var(--u-button-warning-plain-color, var(--u-color-warning));
272
266
  background-color: var(--u-button-warning-plain-bg, var(--u-color-warning-light-9));
273
- border-color: var(--u-button-warning-plain-border, var(--u-color-warning-light-5));
274
- box-shadow: none;
275
267
  }
276
268
  .u-button--color-warning.is-plain:not(.is-disabled):not(.is-loading):hover {
277
- color: var(--u-color-warning);
278
- border-color: var(--u-color-warning);
279
- background-color: var(--u-color-warning-light-9);
280
- }
281
- .u-button--color-warning.is-plain:active {
282
- box-shadow: none;
269
+ background-color: var(--u-color-warning-light-7);
283
270
  }
284
271
  .u-button--color-warning.is-plain.is-disabled {
272
+ border-color: transparent;
285
273
  color: var(--u-color-warning-light-5);
286
- border-color: var(--u-color-warning-light-8);
274
+ background-color: var(--u-color-warning-light-9);
275
+ }
276
+ .u-button--color-warning.is-plain.is-loading {
277
+ border-color: transparent;
278
+ color: var(--u-color-warning-light-5);
279
+ background-color: var(--u-color-warning-light-9);
287
280
  }
288
281
  .u-button .is-ripple-danger {
289
282
  background-color: var(--u-color-danger);
@@ -313,37 +306,37 @@
313
306
  }
314
307
  .u-button--color-danger.is-text {
315
308
  color: var(--u-color-danger);
309
+ border-color: transparent;
316
310
  box-shadow: none;
317
311
  }
318
- .u-button--color-danger.is-text:hover {
319
- box-shadow: 0px 0px 0px 1px var(--u-color-danger);
320
- }
321
312
  .u-button--color-danger.is-text:not(.is-disabled):not(.is-loading):hover, .u-button--color-danger.is-text:not(.is-disabled):not(.is-loading):focus {
322
313
  background-color: var(--u-button-danger-plain-bg, var(--u-color-danger-light-9));
323
314
  }
324
315
  .u-button--color-danger.is-text.is-disabled {
316
+ border-color: transparent;
325
317
  color: var(--u-color-danger-light-5);
326
318
  }
327
319
  .u-button--color-danger.is-text.is-loading {
320
+ border-color: transparent;
328
321
  color: var(--u-color-danger-light-5);
329
322
  }
330
323
  .u-button--color-danger.is-plain {
324
+ border-color: transparent;
331
325
  color: var(--u-button-danger-plain-color, var(--u-color-danger));
332
326
  background-color: var(--u-button-danger-plain-bg, var(--u-color-danger-light-9));
333
- border-color: var(--u-button-danger-plain-border, var(--u-color-danger-light-5));
334
- box-shadow: none;
335
327
  }
336
328
  .u-button--color-danger.is-plain:not(.is-disabled):not(.is-loading):hover {
337
- color: var(--u-color-danger);
338
- border-color: var(--u-color-danger);
339
- background-color: var(--u-color-danger-light-9);
340
- }
341
- .u-button--color-danger.is-plain:active {
342
- box-shadow: none;
329
+ background-color: var(--u-color-danger-light-7);
343
330
  }
344
331
  .u-button--color-danger.is-plain.is-disabled {
332
+ border-color: transparent;
345
333
  color: var(--u-color-danger-light-5);
346
- border-color: var(--u-color-danger-light-8);
334
+ background-color: var(--u-color-danger-light-9);
335
+ }
336
+ .u-button--color-danger.is-plain.is-loading {
337
+ border-color: transparent;
338
+ color: var(--u-color-danger-light-5);
339
+ background-color: var(--u-color-danger-light-9);
347
340
  }
348
341
  .u-button .is-ripple-info {
349
342
  background-color: var(--u-color-info);
@@ -373,37 +366,37 @@
373
366
  }
374
367
  .u-button--color-info.is-text {
375
368
  color: var(--u-color-info);
369
+ border-color: transparent;
376
370
  box-shadow: none;
377
371
  }
378
- .u-button--color-info.is-text:hover {
379
- box-shadow: 0px 0px 0px 1px var(--u-color-info);
380
- }
381
372
  .u-button--color-info.is-text:not(.is-disabled):not(.is-loading):hover, .u-button--color-info.is-text:not(.is-disabled):not(.is-loading):focus {
382
373
  background-color: var(--u-button-info-plain-bg, var(--u-color-info-light-9));
383
374
  }
384
375
  .u-button--color-info.is-text.is-disabled {
376
+ border-color: transparent;
385
377
  color: var(--u-color-info-light-5);
386
378
  }
387
379
  .u-button--color-info.is-text.is-loading {
380
+ border-color: transparent;
388
381
  color: var(--u-color-info-light-5);
389
382
  }
390
383
  .u-button--color-info.is-plain {
384
+ border-color: transparent;
391
385
  color: var(--u-button-info-plain-color, var(--u-color-info));
392
386
  background-color: var(--u-button-info-plain-bg, var(--u-color-info-light-9));
393
- border-color: var(--u-button-info-plain-border, var(--u-color-info-light-5));
394
- box-shadow: none;
395
387
  }
396
388
  .u-button--color-info.is-plain:not(.is-disabled):not(.is-loading):hover {
397
- color: var(--u-color-info);
398
- border-color: var(--u-color-info);
399
- background-color: var(--u-color-info-light-9);
400
- }
401
- .u-button--color-info.is-plain:active {
402
- box-shadow: none;
389
+ background-color: var(--u-color-info-light-7);
403
390
  }
404
391
  .u-button--color-info.is-plain.is-disabled {
392
+ border-color: transparent;
405
393
  color: var(--u-color-info-light-5);
406
- border-color: var(--u-color-info-light-8);
394
+ background-color: var(--u-color-info-light-9);
395
+ }
396
+ .u-button--color-info.is-plain.is-loading {
397
+ border-color: transparent;
398
+ color: var(--u-color-info-light-5);
399
+ background-color: var(--u-color-info-light-9);
407
400
  }
408
401
  .u-button .is-ripple-disabled {
409
402
  background-color: var(--u-color-disabled);
@@ -433,37 +426,37 @@
433
426
  }
434
427
  .u-button--color-disabled.is-text {
435
428
  color: var(--u-color-disabled);
429
+ border-color: transparent;
436
430
  box-shadow: none;
437
431
  }
438
- .u-button--color-disabled.is-text:hover {
439
- box-shadow: 0px 0px 0px 1px var(--u-color-disabled);
440
- }
441
432
  .u-button--color-disabled.is-text:not(.is-disabled):not(.is-loading):hover, .u-button--color-disabled.is-text:not(.is-disabled):not(.is-loading):focus {
442
433
  background-color: var(--u-button-disabled-plain-bg, var(--u-color-disabled-light-9));
443
434
  }
444
435
  .u-button--color-disabled.is-text.is-disabled {
436
+ border-color: transparent;
445
437
  color: var(--u-color-disabled-light-5);
446
438
  }
447
439
  .u-button--color-disabled.is-text.is-loading {
440
+ border-color: transparent;
448
441
  color: var(--u-color-disabled-light-5);
449
442
  }
450
443
  .u-button--color-disabled.is-plain {
444
+ border-color: transparent;
451
445
  color: var(--u-button-disabled-plain-color, var(--u-color-disabled));
452
446
  background-color: var(--u-button-disabled-plain-bg, var(--u-color-disabled-light-9));
453
- border-color: var(--u-button-disabled-plain-border, var(--u-color-disabled-light-5));
454
- box-shadow: none;
455
447
  }
456
448
  .u-button--color-disabled.is-plain:not(.is-disabled):not(.is-loading):hover {
457
- color: var(--u-color-disabled);
458
- border-color: var(--u-color-disabled);
459
- background-color: var(--u-color-disabled-light-9);
460
- }
461
- .u-button--color-disabled.is-plain:active {
462
- box-shadow: none;
449
+ background-color: var(--u-color-disabled-light-7);
463
450
  }
464
451
  .u-button--color-disabled.is-plain.is-disabled {
452
+ border-color: transparent;
465
453
  color: var(--u-color-disabled-light-5);
466
- border-color: var(--u-color-disabled-light-8);
454
+ background-color: var(--u-color-disabled-light-9);
455
+ }
456
+ .u-button--color-disabled.is-plain.is-loading {
457
+ border-color: transparent;
458
+ color: var(--u-color-disabled-light-5);
459
+ background-color: var(--u-color-disabled-light-9);
467
460
  }
468
461
  .u-button .is-ripple-default {
469
462
  background-color: var(--u-color-default);
@@ -493,37 +486,37 @@
493
486
  }
494
487
  .u-button--color-default.is-text {
495
488
  color: var(--u-color-default);
489
+ border-color: transparent;
496
490
  box-shadow: none;
497
491
  }
498
- .u-button--color-default.is-text:hover {
499
- box-shadow: 0px 0px 0px 1px var(--u-color-default);
500
- }
501
492
  .u-button--color-default.is-text:not(.is-disabled):not(.is-loading):hover, .u-button--color-default.is-text:not(.is-disabled):not(.is-loading):focus {
502
493
  background-color: var(--u-button-default-plain-bg, var(--u-color-default-light-9));
503
494
  }
504
495
  .u-button--color-default.is-text.is-disabled {
496
+ border-color: transparent;
505
497
  color: var(--u-color-default-light-5);
506
498
  }
507
499
  .u-button--color-default.is-text.is-loading {
500
+ border-color: transparent;
508
501
  color: var(--u-color-default-light-5);
509
502
  }
510
503
  .u-button--color-default.is-plain {
504
+ border-color: transparent;
511
505
  color: var(--u-button-default-plain-color, var(--u-color-default));
512
506
  background-color: var(--u-button-default-plain-bg, var(--u-color-default-light-9));
513
- border-color: var(--u-button-default-plain-border, var(--u-color-default-light-5));
514
- box-shadow: none;
515
507
  }
516
508
  .u-button--color-default.is-plain:not(.is-disabled):not(.is-loading):hover {
517
- color: var(--u-color-default);
518
- border-color: var(--u-color-default);
519
- background-color: var(--u-color-default-light-9);
520
- }
521
- .u-button--color-default.is-plain:active {
522
- box-shadow: none;
509
+ background-color: var(--u-color-default-light-7);
523
510
  }
524
511
  .u-button--color-default.is-plain.is-disabled {
512
+ border-color: transparent;
525
513
  color: var(--u-color-default-light-5);
526
- border-color: var(--u-color-default-light-8);
514
+ background-color: var(--u-color-default-light-9);
515
+ }
516
+ .u-button--color-default.is-plain.is-loading {
517
+ border-color: transparent;
518
+ color: var(--u-color-default-light-5);
519
+ background-color: var(--u-color-default-light-9);
527
520
  }
528
521
  .u-button.is-circle {
529
522
  border-radius: 50%;
@@ -1,6 +1,6 @@
1
1
  import _sfc_main$1 from "../icon/icon.js";
2
2
  import { CollapseDIKey } from "./di.js";
3
- import { computed, createBlock, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderSlot, resolveDynamicComponent, toDisplayString, unref, watch, withCtx, withKeys, withModifiers } from "vue";
3
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, nextTick, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderSlot, resolveDynamicComponent, toDisplayString, unref, watch, withCtx, withKeys, withModifiers } from "vue";
4
4
  import { useModel as useModel$1 } from "@veltra/compositions";
5
5
  import { ExpandTransition, bem } from "@veltra/utils";
6
6
  import { ArrowDown } from "@veltra/icons/normal";
@@ -23,7 +23,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
23
23
  },
24
24
  title: {},
25
25
  disabled: { type: Boolean },
26
- expandIcon: {}
26
+ expandIcon: {},
27
+ destroyOnCollapse: { type: Boolean }
27
28
  },
28
29
  emits: ["update:modelValue", "change"],
29
30
  setup(__props, { emit: __emit }) {
@@ -64,14 +65,27 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
64
65
  emit("change", next);
65
66
  };
66
67
  const wrapperEl = ref();
68
+ /** 内容是否挂载:destroyOnCollapse 时折叠态卸载内容 DOM,展开前重新挂载 */
69
+ const contentMounted = ref(isActive.value || !props.destroyOnCollapse);
67
70
  onMounted(() => {
68
71
  if (context && props.value !== void 0) context.register(props.value);
69
72
  if (!wrapperEl.value) return;
70
73
  expandTransition.setExpanded(wrapperEl.value, isActive.value);
71
74
  });
72
- watch(isActive, (active) => {
75
+ watch(isActive, async (active) => {
76
+ if (active) {
77
+ if (!contentMounted.value) {
78
+ contentMounted.value = true;
79
+ await nextTick();
80
+ }
81
+ if (!wrapperEl.value || !isActive.value) return;
82
+ expandTransition.expand(wrapperEl.value);
83
+ return;
84
+ }
73
85
  if (!wrapperEl.value) return;
74
- active ? expandTransition.expand(wrapperEl.value) : expandTransition.collapse(wrapperEl.value);
86
+ expandTransition.collapse(wrapperEl.value, () => {
87
+ if (!isActive.value && props.destroyOnCollapse) contentMounted.value = false;
88
+ });
75
89
  });
76
90
  onBeforeUnmount(() => {
77
91
  if (context && props.value !== void 0) context.unregister(props.value);
@@ -87,15 +101,18 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
87
101
  tabindex: __props.disabled ? -1 : 0,
88
102
  onClick: handleClick,
89
103
  onKeydown: [withKeys(withModifiers(handleClick, ["prevent"]), ["enter"]), withKeys(withModifiers(handleClick, ["prevent"]), ["space"])]
90
- }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("title")) }, [renderSlot(_ctx.$slots, "title", {}, () => [createTextVNode(toDisplayString(__props.title), 1)])], 2), createElementVNode("span", { class: normalizeClass(unref(cls).e("icon")) }, [renderSlot(_ctx.$slots, "icon", { isActive: isActive.value }, () => [createVNode(unref(_sfc_main$1), null, {
104
+ }, [renderSlot(_ctx.$slots, "header", { isActive: isActive.value }, () => [createElementVNode("span", { class: normalizeClass(unref(cls).e("title")) }, toDisplayString(__props.title), 3), createElementVNode("span", { class: normalizeClass(unref(cls).e("icon")) }, [createVNode(unref(_sfc_main$1), null, {
91
105
  default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(iconComponent.value)))]),
92
106
  _: 1
93
- })])], 2)], 42, _hoisted_1), createElementVNode("div", {
107
+ })], 2)])], 42, _hoisted_1), createElementVNode("div", {
94
108
  ref_key: "wrapperEl",
95
109
  ref: wrapperEl,
96
110
  class: normalizeClass(unref(cls).e("content-wrapper")),
97
111
  "aria-hidden": !isActive.value
98
- }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("content")) }, [renderSlot(_ctx.$slots, "default")], 2)], 10, _hoisted_2)], 2);
112
+ }, [contentMounted.value ? (openBlock(), createElementBlock("div", {
113
+ key: 0,
114
+ class: normalizeClass(unref(cls).e("content"))
115
+ }, [renderSlot(_ctx.$slots, "default")], 2)) : createCommentVNode("v-if", true)], 10, _hoisted_2)], 2);
99
116
  };
100
117
  }
101
118
  });
@@ -1 +1 @@
1
- {"version":3,"file":"collapse-item.js","names":[],"sources":["../../../src/components/collapse/collapse-item.vue"],"sourcesContent":["<template>\n <div :class=\"classList\">\n <div\n :class=\"headerClassList\"\n role=\"button\"\n :aria-expanded=\"isActive\"\n :aria-disabled=\"disabled\"\n :tabindex=\"disabled ? -1 : 0\"\n @click=\"handleClick\"\n @keydown.enter.prevent=\"handleClick\"\n @keydown.space.prevent=\"handleClick\"\n >\n <span :class=\"cls.e('title')\">\n <slot name=\"title\">{{ title }}</slot>\n </span>\n\n <span :class=\"cls.e('icon')\">\n <slot name=\"icon\" :is-active=\"isActive\">\n <UIcon><component :is=\"iconComponent\" /></UIcon>\n </slot>\n </span>\n </div>\n <div ref=\"wrapperEl\" :class=\"cls.e('content-wrapper')\" :aria-hidden=\"!isActive\">\n <div :class=\"cls.e('content')\">\n <slot />\n </div>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useModel } from '@veltra/compositions'\nimport { ArrowDown } from '@veltra/icons/normal'\nimport { bem, ExpandTransition } from '@veltra/utils'\nimport { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { CollapseItemEmits, CollapseItemProps } from '../../types'\nimport { UIcon } from '../icon'\nimport { CollapseDIKey } from './di'\n\ndefineOptions({ name: 'UCollapseItem' })\n\nconst props = withDefaults(defineProps<CollapseItemProps>(), { modelValue: false })\nconst emit = defineEmits<CollapseItemEmits>()\n\nconst context = inject(CollapseDIKey, undefined)\n\nconst cls = context?.cls ?? bem('collapse')\n\nconst standaloneModel = useModel<CollapseItemProps, 'modelValue'>({\n props,\n emit,\n defaultValue: false\n})\n\nconst isActive = computed(() => {\n if (context) {\n return props.value !== undefined && context.activeValues.value.includes(props.value)\n }\n return !!standaloneModel.value\n})\n\nconst iconComponent = computed(() => context?.expandIcon.value ?? props.expandIcon ?? ArrowDown)\n\nconst classList = computed(() => [\n cls.e('item'),\n bem.is('active', isActive.value),\n bem.is('disabled', props.disabled)\n])\n\nconst headerClassList = computed(() => [\n cls.e('header'),\n bem.is('disabled', props.disabled),\n bem.is('active', isActive.value)\n])\n\nconst expandTransition =\n context?.expandTransition ??\n new ExpandTransition({ transition: 'height 0.24s cubic-bezier(0.4, 0, 0.2, 1)' })\n\nconst handleClick = () => {\n if (props.disabled) return\n\n if (context) {\n if (props.value === undefined) return\n context.toggle(props.value)\n return\n }\n\n const next = !standaloneModel.value\n standaloneModel.value = next\n emit('change', next)\n}\n\nconst wrapperEl = ref<HTMLElement>()\n\nonMounted(() => {\n if (context && props.value !== undefined) {\n context.register(props.value)\n }\n if (!wrapperEl.value) return\n expandTransition.setExpanded(wrapperEl.value, isActive.value)\n})\n\nwatch(isActive, (active) => {\n if (!wrapperEl.value) return\n active ? expandTransition.expand(wrapperEl.value) : expandTransition.collapse(wrapperEl.value)\n})\n\nonBeforeUnmount(() => {\n if (context && props.value !== undefined) {\n context.unregister(props.value)\n }\n if (!wrapperEl.value) return\n expandTransition.cancel(wrapperEl.value)\n})\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"collapse-item.js","names":[],"sources":["../../../src/components/collapse/collapse-item.vue"],"sourcesContent":["<template>\n <div :class=\"classList\">\n <div\n :class=\"headerClassList\"\n role=\"button\"\n :aria-expanded=\"isActive\"\n :aria-disabled=\"disabled\"\n :tabindex=\"disabled ? -1 : 0\"\n @click=\"handleClick\"\n @keydown.enter.prevent=\"handleClick\"\n @keydown.space.prevent=\"handleClick\"\n >\n <slot name=\"header\" :is-active=\"isActive\">\n <span :class=\"cls.e('title')\">{{ title }}</span>\n\n <span :class=\"cls.e('icon')\">\n <UIcon><component :is=\"iconComponent\" /></UIcon>\n </span>\n </slot>\n </div>\n <div ref=\"wrapperEl\" :class=\"cls.e('content-wrapper')\" :aria-hidden=\"!isActive\">\n <div v-if=\"contentMounted\" :class=\"cls.e('content')\">\n <slot />\n </div>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useModel } from '@veltra/compositions'\nimport { ArrowDown } from '@veltra/icons/normal'\nimport { bem, ExpandTransition } from '@veltra/utils'\nimport { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { CollapseItemEmits, CollapseItemProps } from '../../types'\nimport { UIcon } from '../icon'\nimport { CollapseDIKey } from './di'\n\ndefineOptions({ name: 'UCollapseItem' })\n\nconst props = withDefaults(defineProps<CollapseItemProps>(), { modelValue: false })\nconst emit = defineEmits<CollapseItemEmits>()\n\ndefineSlots<{\n /** 自定义整个头部,作用域参数 `isActive` 指示展开状态;缺省渲染 title + 展开图标 */\n header?: (scope: { isActive: boolean }) => any\n /** 折叠面板内容 */\n default?: () => any\n}>()\n\nconst context = inject(CollapseDIKey, undefined)\n\nconst cls = context?.cls ?? bem('collapse')\n\nconst standaloneModel = useModel<CollapseItemProps, 'modelValue'>({\n props,\n emit,\n defaultValue: false\n})\n\nconst isActive = computed(() => {\n if (context) {\n return props.value !== undefined && context.activeValues.value.includes(props.value)\n }\n return !!standaloneModel.value\n})\n\nconst iconComponent = computed(() => context?.expandIcon.value ?? props.expandIcon ?? ArrowDown)\n\nconst classList = computed(() => [\n cls.e('item'),\n bem.is('active', isActive.value),\n bem.is('disabled', props.disabled)\n])\n\nconst headerClassList = computed(() => [\n cls.e('header'),\n bem.is('disabled', props.disabled),\n bem.is('active', isActive.value)\n])\n\nconst expandTransition =\n context?.expandTransition ??\n new ExpandTransition({ transition: 'height 0.24s cubic-bezier(0.4, 0, 0.2, 1)' })\n\nconst handleClick = () => {\n if (props.disabled) return\n\n if (context) {\n if (props.value === undefined) return\n context.toggle(props.value)\n return\n }\n\n const next = !standaloneModel.value\n standaloneModel.value = next\n emit('change', next)\n}\n\nconst wrapperEl = ref<HTMLElement>()\n\n/** 内容是否挂载:destroyOnCollapse 时折叠态卸载内容 DOM,展开前重新挂载 */\nconst contentMounted = ref(isActive.value || !props.destroyOnCollapse)\n\nonMounted(() => {\n if (context && props.value !== undefined) {\n context.register(props.value)\n }\n if (!wrapperEl.value) return\n expandTransition.setExpanded(wrapperEl.value, isActive.value)\n})\n\nwatch(isActive, async (active) => {\n if (active) {\n // 展开前先挂载内容,待 DOM 更新后再测量高度驱动展开动画\n if (!contentMounted.value) {\n contentMounted.value = true\n await nextTick()\n }\n if (!wrapperEl.value || !isActive.value) return\n expandTransition.expand(wrapperEl.value)\n return\n }\n if (!wrapperEl.value) return\n // 收起动画落定后再卸载内容 DOM;期间被重新展开则不卸载\n expandTransition.collapse(wrapperEl.value, () => {\n if (!isActive.value && props.destroyOnCollapse) contentMounted.value = false\n })\n})\n\nonBeforeUnmount(() => {\n if (context && props.value !== undefined) {\n context.unregister(props.value)\n }\n if (!wrapperEl.value) return\n expandTransition.cancel(wrapperEl.value)\n})\n</script>\n"],"mappings":"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"}
@@ -88,11 +88,13 @@
88
88
  cursor: text;
89
89
  }
90
90
  .u-dialog__body {
91
- padding: 8px;
92
91
  background-color: var(--u-bg-color-top);
93
92
  backdrop-filter: var(--u-bg-filter);
94
93
  flex-grow: 1;
95
94
  }
95
+ .u-dialog__body .u-scroll__content {
96
+ padding: 8px;
97
+ }
96
98
  .u-dialog__footer {
97
99
  border-top: var(--u-border);
98
100
  padding: 8px;
@@ -12,10 +12,13 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
12
12
  gap: {},
13
13
  cols: {},
14
14
  rows: {},
15
- resizable: { type: Boolean }
15
+ resizable: { type: Boolean },
16
+ colMinSizes: {}
16
17
  },
17
- setup(__props) {
18
+ emits: ["resize-start", "resize-end"],
19
+ setup(__props, { emit: __emit }) {
18
20
  const props = __props;
21
+ const emit = __emit;
19
22
  const cls = bem("layout");
20
23
  const templateCols = ref([]);
21
24
  watchEffect(() => {
@@ -52,18 +55,22 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
52
55
  () => props.cols
53
56
  ], () => {
54
57
  nextTick(() => {
55
- getResizeOffsets();
58
+ syncResizers();
56
59
  });
57
60
  });
58
61
  useResizeObserver({
59
62
  targets: containerRef,
60
63
  onResize() {
61
- getResizeOffsets();
62
- resizerRefs.value.forEach((r, i) => {
63
- r.update(resizerOffsets.value[i]);
64
- });
64
+ syncResizers();
65
65
  }
66
66
  });
67
+ /** 重新测量各列边界并同步所有拖拽手柄位置(容器尺寸或 cols 程序化变化后调用) */
68
+ function syncResizers() {
69
+ getResizeOffsets();
70
+ resizerRefs.value.forEach((r, i) => {
71
+ r.update(resizerOffsets.value[i]);
72
+ });
73
+ }
67
74
  let prevSize = 0;
68
75
  let nextSize = 0;
69
76
  const handleStartResize = (index) => {
@@ -73,9 +80,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
73
80
  const nextDom = container.children[index + 1];
74
81
  prevSize = prevDom ? prevDom.getBoundingClientRect().width : 0;
75
82
  nextSize = nextDom ? nextDom.getBoundingClientRect().width : 0;
83
+ emit("resize-start", index);
76
84
  };
77
85
  const handleResize = (offset, index) => {
78
- const clamped = Math.max(-prevSize, Math.min(nextSize, offset));
86
+ const minPrev = props.colMinSizes?.[index] ?? 0;
87
+ const minNext = props.colMinSizes?.[index + 1] ?? 0;
88
+ const clamped = Math.max(minPrev - prevSize, Math.min(nextSize - minNext, offset));
79
89
  templateCols.value[index] = `${prevSize + clamped}px`;
80
90
  templateCols.value[index + 1] = `${nextSize - clamped}px`;
81
91
  };
@@ -99,11 +109,13 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
99
109
  ref: resizerRefs,
100
110
  direction: "vertical",
101
111
  onResize: ($event) => handleResize($event, index),
102
- onResizeStart: ($event) => handleStartResize(index)
112
+ onResizeStart: ($event) => handleStartResize(index),
113
+ onResizeEnd: ($event) => emit("resize-end", index)
103
114
  }, null, 8, [
104
115
  "offset",
105
116
  "onResize",
106
- "onResizeStart"
117
+ "onResizeStart",
118
+ "onResizeEnd"
107
119
  ]);
108
120
  }), 128)),
109
121
  createCommentVNode(" 横向调节 "),
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","names":[],"sources":["../../../src/components/layout/layout.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" :class=\"cls.b\" :style=\"style\" ref=\"containerRef\">\n <slot />\n\n <!-- 竖向调节 -->\n <ULayoutResizer\n v-for=\"(offset, index) of resizerOffsets\"\n :offset=\"offset\"\n :key=\"index\"\n ref=\"resizerRefs\"\n direction=\"vertical\"\n @resize=\"handleResize($event, index)\"\n @resize-start=\"handleStartResize(index)\"\n >\n </ULayoutResizer>\n\n <!-- 横向调节 -->\n <!-- <ULayoutResizer\n v-for=\"item in horizontalResizerList\"\n :key=\"item\"\n direction=\"horizontal\"\n /> -->\n </component>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useResizeObserver } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport {\n computed,\n shallowRef,\n type CSSProperties,\n provide,\n watchEffect,\n ref,\n watch,\n nextTick\n} from 'vue'\n\nimport type { LayoutProps } from '../../types'\nimport { LayoutDIKey } from './di'\nimport ULayoutResizer from './layout-resizer.vue'\n\ndefineOptions({ name: 'ULayout' })\n\nconst props = withDefaults(defineProps<LayoutProps>(), { tag: 'div' })\n\nconst cls = bem('layout')\n\nconst templateCols = ref<string[]>([])\n\nwatchEffect(() => {\n const { cols } = props\n if (!cols) {\n templateCols.value = []\n return\n }\n templateCols.value = typeof cols === 'string' ? cols.split(' ') : [...cols]\n})\n\nconst style = computed<CSSProperties>(() => {\n const { rows, gap, resizable } = props\n return {\n gridTemplateColumns: templateCols.value.join(' '),\n gridTemplateRows: rows ? (typeof rows === 'string' ? rows : rows.join(' ')) : '',\n columnGap: resizable ? '10px' : withUnit(gap, 'px')\n }\n})\n\nconst containerRef = shallowRef<HTMLElement>()\nconst resizerRefs = shallowRef<InstanceType<typeof ULayoutResizer>[]>([])\n\nconst resizerOffsets = ref<number[]>([])\n\nfunction getResizeOffsets() {\n const container = containerRef.value\n if (!props.resizable || !container || !props.cols) {\n return (resizerOffsets.value = [])\n }\n\n resizerOffsets.value = templateCols.value.slice(0, -1).map((_, index) => {\n const dom = container.children[index] as HTMLElement\n if (!dom) return 0\n return dom.offsetLeft + dom.offsetWidth\n })\n}\n\nwatch([() => props.resizable, containerRef, () => props.cols], () => {\n nextTick(() => {\n getResizeOffsets()\n })\n})\n\nuseResizeObserver({\n targets: containerRef,\n onResize() {\n getResizeOffsets()\n resizerRefs.value.forEach((r, i) => {\n r.update(resizerOffsets.value![i]!)\n })\n }\n})\n\n// const horizontalResizerList = []\n\n// const resizing = shallowRef(false)\n\nlet prevSize = 0\nlet nextSize = 0\nconst handleStartResize = (index: number) => {\n const container = containerRef.value\n if (!container) return\n\n // 拖拽前把相邻两列按实际渲染宽度固定成 px,\n // 这样拖拽时两列等量增减、容器整体宽度保持不变。\n const prevDom = container.children[index] as HTMLElement | undefined\n const nextDom = container.children[index + 1] as HTMLElement | undefined\n prevSize = prevDom ? prevDom.getBoundingClientRect().width : 0\n nextSize = nextDom ? nextDom.getBoundingClientRect().width : 0\n}\nconst handleResize = (offset: number, index: number) => {\n // 钳位到 [-prevSize, nextSize],避免轨道尺寸出现负值——\n // 一旦 grid-template-columns 中带负值,整条声明会被浏览器丢弃。\n const clamped = Math.max(-prevSize, Math.min(nextSize, offset))\n templateCols.value[index] = `${prevSize + clamped}px`\n templateCols.value[index + 1] = `${nextSize - clamped}px`\n}\n\nprovide(LayoutDIKey, { cls })\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;EA6CA,MAAM,QAAQ;EAEd,MAAM,MAAM,IAAI,QAAQ;EAExB,MAAM,eAAe,IAAc,CAAC,CAAC;EAErC,kBAAkB;GAChB,MAAM,EAAE,SAAS;GACjB,IAAI,CAAC,MAAM;IACT,aAAa,QAAQ,CAAC;IACtB;GACF;GACA,aAAa,QAAQ,OAAO,SAAS,WAAW,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI;EAC5E,CAAC;EAED,MAAM,QAAQ,eAA8B;GAC1C,MAAM,EAAE,MAAM,KAAK,cAAc;GACjC,OAAO;IACL,qBAAqB,aAAa,MAAM,KAAK,GAAG;IAChD,kBAAkB,OAAQ,OAAO,SAAS,WAAW,OAAO,KAAK,KAAK,GAAG,IAAK;IAC9E,WAAW,YAAY,SAAS,SAAS,KAAK,IAAI;GACpD;EACF,CAAC;EAED,MAAM,eAAe,WAAwB;EAC7C,MAAM,cAAc,WAAkD,CAAC,CAAC;EAExE,MAAM,iBAAiB,IAAc,CAAC,CAAC;EAEvC,SAAS,mBAAmB;GAC1B,MAAM,YAAY,aAAa;GAC/B,IAAI,CAAC,MAAM,aAAa,CAAC,aAAa,CAAC,MAAM,MAC3C,OAAQ,eAAe,QAAQ,CAAC;GAGlC,eAAe,QAAQ,aAAa,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,GAAG,UAAU;IACvE,MAAM,MAAM,UAAU,SAAS;IAC/B,IAAI,CAAC,KAAK,OAAO;IACjB,OAAO,IAAI,aAAa,IAAI;GAC9B,CAAC;EACH;EAEA,MAAM;SAAO,MAAM;GAAW;SAAoB,MAAM;EAAI,SAAS;GACnE,eAAe;IACb,iBAAiB;GACnB,CAAC;EACH,CAAC;EAED,kBAAkB;GAChB,SAAS;GACT,WAAW;IACT,iBAAiB;IACjB,YAAY,MAAM,SAAS,GAAG,MAAM;KAClC,EAAE,OAAO,eAAe,MAAO,EAAG;IACpC,CAAC;GACH;EACF,CAAC;EAMD,IAAI,WAAW;EACf,IAAI,WAAW;EACf,MAAM,qBAAqB,UAAkB;GAC3C,MAAM,YAAY,aAAa;GAC/B,IAAI,CAAC,WAAW;GAIhB,MAAM,UAAU,UAAU,SAAS;GACnC,MAAM,UAAU,UAAU,SAAS,QAAQ;GAC3C,WAAW,UAAU,QAAQ,sBAAsB,CAAC,CAAC,QAAQ;GAC7D,WAAW,UAAU,QAAQ,sBAAsB,CAAC,CAAC,QAAQ;EAC/D;EACA,MAAM,gBAAgB,QAAgB,UAAkB;GAGtD,MAAM,UAAU,KAAK,IAAI,CAAC,UAAU,KAAK,IAAI,UAAU,MAAM,CAAC;GAC9D,aAAa,MAAM,SAAS,GAAG,WAAW,QAAQ;GAClD,aAAa,MAAM,QAAQ,KAAK,GAAG,WAAW,QAAQ;EACxD;EAEA,QAAQ,aAAa,EAAE,IAAI,CAAC;;GA/H1B,OAAA,UAAA,GAAA,YAqBY,wBArBI,QAAA,GAAG,GAAA;IAAG,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,CAAC;IAAG,OAAK,eAAE,MAAA,KAAK;IAAM,SAAA;IAAJ,KAAI;;IAC7C,SAAA,cAAA;KAAR,WAAQ,KAAA,QAAA,SAAA;KAER,mBAAA,QAAA;MACA,UAAA,IAAA,GAAA,mBASiB,UAAA,MAAA,WARW,eAAA,QAAlB,QAAQ,UAAK;MADvB,OAAA,UAAA,GAAA,YASiB,aAAA;OAPN;OACR,KAAK;;OACF,SAAA;OAAJ,KAAI;OACJ,WAAU;OACT,WAAM,WAAE,aAAa,QAAQ,KAAK;OAClC,gBAAY,WAAE,kBAAkB,KAAK;;;;;;;KAIxC,mBAAA,QAAA;KACA,mBAAA,+HAAA"}
1
+ {"version":3,"file":"layout.js","names":[],"sources":["../../../src/components/layout/layout.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" :class=\"cls.b\" :style=\"style\" ref=\"containerRef\">\n <slot />\n\n <!-- 竖向调节 -->\n <ULayoutResizer\n v-for=\"(offset, index) of resizerOffsets\"\n :offset=\"offset\"\n :key=\"index\"\n ref=\"resizerRefs\"\n direction=\"vertical\"\n @resize=\"handleResize($event, index)\"\n @resize-start=\"handleStartResize(index)\"\n @resize-end=\"emit('resize-end', index)\"\n >\n </ULayoutResizer>\n\n <!-- 横向调节 -->\n <!-- <ULayoutResizer\n v-for=\"item in horizontalResizerList\"\n :key=\"item\"\n direction=\"horizontal\"\n /> -->\n </component>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useResizeObserver } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport {\n computed,\n shallowRef,\n type CSSProperties,\n provide,\n watchEffect,\n ref,\n watch,\n nextTick\n} from 'vue'\n\nimport type { LayoutEmits, LayoutProps } from '../../types'\nimport { LayoutDIKey } from './di'\nimport ULayoutResizer from './layout-resizer.vue'\n\ndefineOptions({ name: 'ULayout' })\n\nconst props = withDefaults(defineProps<LayoutProps>(), { tag: 'div' })\n\nconst emit = defineEmits<LayoutEmits>()\n\nconst cls = bem('layout')\n\nconst templateCols = ref<string[]>([])\n\nwatchEffect(() => {\n const { cols } = props\n if (!cols) {\n templateCols.value = []\n return\n }\n templateCols.value = typeof cols === 'string' ? cols.split(' ') : [...cols]\n})\n\nconst style = computed<CSSProperties>(() => {\n const { rows, gap, resizable } = props\n return {\n gridTemplateColumns: templateCols.value.join(' '),\n gridTemplateRows: rows ? (typeof rows === 'string' ? rows : rows.join(' ')) : '',\n columnGap: resizable ? '10px' : withUnit(gap, 'px')\n }\n})\n\nconst containerRef = shallowRef<HTMLElement>()\nconst resizerRefs = shallowRef<InstanceType<typeof ULayoutResizer>[]>([])\n\nconst resizerOffsets = ref<number[]>([])\n\nfunction getResizeOffsets() {\n const container = containerRef.value\n if (!props.resizable || !container || !props.cols) {\n return (resizerOffsets.value = [])\n }\n\n resizerOffsets.value = templateCols.value.slice(0, -1).map((_, index) => {\n const dom = container.children[index] as HTMLElement\n if (!dom) return 0\n return dom.offsetLeft + dom.offsetWidth\n })\n}\n\nwatch([() => props.resizable, containerRef, () => props.cols], () => {\n nextTick(() => {\n syncResizers()\n })\n})\n\nuseResizeObserver({\n targets: containerRef,\n onResize() {\n syncResizers()\n }\n})\n\n/** 重新测量各列边界并同步所有拖拽手柄位置(容器尺寸或 cols 程序化变化后调用) */\nfunction syncResizers() {\n getResizeOffsets()\n resizerRefs.value.forEach((r, i) => {\n r.update(resizerOffsets.value![i]!)\n })\n}\n\n// const horizontalResizerList = []\n\n// const resizing = shallowRef(false)\n\nlet prevSize = 0\nlet nextSize = 0\nconst handleStartResize = (index: number) => {\n const container = containerRef.value\n if (!container) return\n\n // 拖拽前把相邻两列按实际渲染宽度固定成 px,\n // 这样拖拽时两列等量增减、容器整体宽度保持不变。\n const prevDom = container.children[index] as HTMLElement | undefined\n const nextDom = container.children[index + 1] as HTMLElement | undefined\n prevSize = prevDom ? prevDom.getBoundingClientRect().width : 0\n nextSize = nextDom ? nextDom.getBoundingClientRect().width : 0\n emit('resize-start', index)\n}\nconst handleResize = (offset: number, index: number) => {\n // 钳位到 [colMin-prevSize, nextSize-colMin],避免轨道尺寸出现负值或小于列最小宽度——\n // 一旦 grid-template-columns 中带负值,整条声明会被浏览器丢弃。\n const minPrev = props.colMinSizes?.[index] ?? 0\n const minNext = props.colMinSizes?.[index + 1] ?? 0\n const clamped = Math.max(minPrev - prevSize, Math.min(nextSize - minNext, offset))\n templateCols.value[index] = `${prevSize + clamped}px`\n templateCols.value[index + 1] = `${nextSize - clamped}px`\n}\n\nprovide(LayoutDIKey, { cls })\n</script>\n"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  import { TableResizeKey } from "./di.js";
2
2
  import { allocateLeafColumnWidths } from "./allocate-column-widths.js";
3
- import { nextTick, provide, shallowRef, watchEffect } from "vue";
3
+ import { computed, nextTick, provide, shallowRef, watchEffect } from "vue";
4
+ import { useResizeObserver } from "@veltra/compositions";
5
+ import { debounce } from "@cat-kit/core";
4
6
  //#region src/components/table/use-col-resize.ts
5
7
  function useColResize(options) {
6
8
  const { scrollRef, leafColumns } = options;
@@ -27,6 +29,12 @@ function useColResize(options) {
27
29
  }, 0);
28
30
  }
29
31
  watchEffect(correctColumnStyle);
32
+ const debouncedCorrectColumnStyle = debounce(correctColumnStyle);
33
+ const containerRef = computed(() => scrollRef.value?.containerRef ?? null);
34
+ useResizeObserver({
35
+ targets: containerRef,
36
+ onResize: debouncedCorrectColumnStyle
37
+ });
30
38
  function updateResizeLine(transformX) {
31
39
  if (!resizeLineRef.value) return;
32
40
  resizeLineRef.value.style.transform = `translateX(${transformX}px)`;
@@ -1 +1 @@
1
- {"version":3,"file":"use-col-resize.js","names":[],"sources":["../../../src/components/table/use-col-resize.ts"],"sourcesContent":["import { nextTick, provide, shallowRef, watchEffect, type ShallowRef } from 'vue'\n\nimport type { ScrollExposed } from '../../types'\nimport { allocateLeafColumnWidths } from './allocate-column-widths'\nimport { TableResizeKey } from './di'\nimport type { ColumnNode } from './node/col'\n\ninterface Options {\n scrollRef: ShallowRef<ScrollExposed | undefined>\n leafColumns: ShallowRef<ColumnNode[]>\n}\n\ninterface UseColResizeReturned {\n resizeLineRef: ShallowRef<HTMLElement | undefined>\n colgroupRef: ShallowRef<HTMLElement | undefined>\n showResizeLine: ShallowRef<boolean>\n handleResizeMousedown: (e: MouseEvent, resizeColumn: ColumnNode) => void\n}\n\nexport function useColResize(options: Options): UseColResizeReturned {\n const { scrollRef, leafColumns } = options\n\n const showResizeLine = shallowRef(false)\n\n const resizeLineRef = shallowRef<HTMLElement>()\n const colgroupRef = shallowRef<HTMLElement>()\n const headerRef = shallowRef<HTMLElement>()\n\n /**\n * 修正列样式\n */\n function correctColumnStyle() {\n // 获取表格容器宽度\n const containerWidth = scrollRef.value?.containerRef?.clientWidth\n\n if (!containerWidth || !leafColumns.value.length) return\n\n allocateLeafColumnWidths(leafColumns.value, containerWidth)\n\n const fixedLeft = leafColumns.value.filter((column) => column.fixed === 'left')\n const fixedRight = leafColumns.value.filter((column) => column.fixed === 'right')\n\n fixedLeft.reduce((acc, cur) => {\n cur.style.left = acc\n return acc + (cur.width ?? cur.minWidth!)\n }, 0)\n\n fixedRight.reduceRight((acc, cur) => {\n cur.style.right = acc\n return acc + (cur.width ?? cur.minWidth!)\n }, 0)\n }\n\n watchEffect(correctColumnStyle)\n\n function updateResizeLine(transformX: number): void {\n if (!resizeLineRef.value) return\n resizeLineRef.value.style.transform = `translateX(${transformX}px)`\n }\n\n // 表格相对于页面左边缘的偏移量,\n // 此处作为缓存以减少频繁地调用 getBoundingClientRect 方法\n let containerLeft = 0\n let originX = 0\n let originWidth = 0\n let currentResizeColumn: ColumnNode | null = null\n\n function handleResizeMousedown(e: MouseEvent, resizeColumn: ColumnNode): void {\n const tableEl = scrollRef.value?.el\n if (!tableEl) return\n\n showResizeLine.value = true\n currentResizeColumn = resizeColumn\n originX = e.pageX\n originWidth = resizeColumn.width ?? 0\n\n const { left } = tableEl.getBoundingClientRect()\n containerLeft = left\n\n nextTick(() => {\n updateResizeLine(e.pageX - left)\n })\n\n document.addEventListener('mousemove', handleResizeMousemove)\n document.addEventListener('mouseup', handleResizeMouseup)\n }\n\n function handleResizeMousemove(e: MouseEvent): void {\n updateResizeLine(e.pageX - containerLeft)\n }\n\n function handleResizeMouseup(e: MouseEvent): void {\n currentResizeColumn!.width = Math.max(\n originWidth + e.pageX - originX,\n currentResizeColumn!.minWidth!\n )\n currentResizeColumn!.explicitWidth = true\n\n showResizeLine.value = false\n currentResizeColumn = null\n correctColumnStyle()\n\n document.removeEventListener('mouseup', handleResizeMouseup)\n document.removeEventListener('mousemove', handleResizeMousemove)\n }\n\n provide(TableResizeKey, { handleResizeMousedown, headerRef })\n\n return { resizeLineRef, colgroupRef, showResizeLine, handleResizeMousedown }\n}\n"],"mappings":";;;;AAmBA,SAAgB,aAAa,SAAwC;CACnE,MAAM,EAAE,WAAW,gBAAgB;CAEnC,MAAM,iBAAiB,WAAW,KAAK;CAEvC,MAAM,gBAAgB,WAAwB;CAC9C,MAAM,cAAc,WAAwB;CAC5C,MAAM,YAAY,WAAwB;;;;CAK1C,SAAS,qBAAqB;EAE5B,MAAM,iBAAiB,UAAU,OAAO,cAAc;EAEtD,IAAI,CAAC,kBAAkB,CAAC,YAAY,MAAM,QAAQ;EAElD,yBAAyB,YAAY,OAAO,cAAc;EAE1D,MAAM,YAAY,YAAY,MAAM,QAAQ,WAAW,OAAO,UAAU,MAAM;EAC9E,MAAM,aAAa,YAAY,MAAM,QAAQ,WAAW,OAAO,UAAU,OAAO;EAEhF,UAAU,QAAQ,KAAK,QAAQ;GAC7B,IAAI,MAAM,OAAO;GACjB,OAAO,OAAO,IAAI,SAAS,IAAI;EACjC,GAAG,CAAC;EAEJ,WAAW,aAAa,KAAK,QAAQ;GACnC,IAAI,MAAM,QAAQ;GAClB,OAAO,OAAO,IAAI,SAAS,IAAI;EACjC,GAAG,CAAC;CACN;CAEA,YAAY,kBAAkB;CAE9B,SAAS,iBAAiB,YAA0B;EAClD,IAAI,CAAC,cAAc,OAAO;EAC1B,cAAc,MAAM,MAAM,YAAY,cAAc,WAAW;CACjE;CAIA,IAAI,gBAAgB;CACpB,IAAI,UAAU;CACd,IAAI,cAAc;CAClB,IAAI,sBAAyC;CAE7C,SAAS,sBAAsB,GAAe,cAAgC;EAC5E,MAAM,UAAU,UAAU,OAAO;EACjC,IAAI,CAAC,SAAS;EAEd,eAAe,QAAQ;EACvB,sBAAsB;EACtB,UAAU,EAAE;EACZ,cAAc,aAAa,SAAS;EAEpC,MAAM,EAAE,SAAS,QAAQ,sBAAsB;EAC/C,gBAAgB;EAEhB,eAAe;GACb,iBAAiB,EAAE,QAAQ,IAAI;EACjC,CAAC;EAED,SAAS,iBAAiB,aAAa,qBAAqB;EAC5D,SAAS,iBAAiB,WAAW,mBAAmB;CAC1D;CAEA,SAAS,sBAAsB,GAAqB;EAClD,iBAAiB,EAAE,QAAQ,aAAa;CAC1C;CAEA,SAAS,oBAAoB,GAAqB;EAChD,oBAAqB,QAAQ,KAAK,IAChC,cAAc,EAAE,QAAQ,SACxB,oBAAqB,QACvB;EACA,oBAAqB,gBAAgB;EAErC,eAAe,QAAQ;EACvB,sBAAsB;EACtB,mBAAmB;EAEnB,SAAS,oBAAoB,WAAW,mBAAmB;EAC3D,SAAS,oBAAoB,aAAa,qBAAqB;CACjE;CAEA,QAAQ,gBAAgB;EAAE;EAAuB;CAAU,CAAC;CAE5D,OAAO;EAAE;EAAe;EAAa;EAAgB;CAAsB;AAC7E"}
1
+ {"version":3,"file":"use-col-resize.js","names":[],"sources":["../../../src/components/table/use-col-resize.ts"],"sourcesContent":["import { debounce } from '@cat-kit/core'\nimport { useResizeObserver } from '@veltra/compositions'\nimport { computed, nextTick, provide, shallowRef, watchEffect, type ShallowRef } from 'vue'\n\nimport type { ScrollExposed } from '../../types'\nimport { allocateLeafColumnWidths } from './allocate-column-widths'\nimport { TableResizeKey } from './di'\nimport type { ColumnNode } from './node/col'\n\ninterface Options {\n scrollRef: ShallowRef<ScrollExposed | undefined>\n leafColumns: ShallowRef<ColumnNode[]>\n}\n\ninterface UseColResizeReturned {\n resizeLineRef: ShallowRef<HTMLElement | undefined>\n colgroupRef: ShallowRef<HTMLElement | undefined>\n showResizeLine: ShallowRef<boolean>\n handleResizeMousedown: (e: MouseEvent, resizeColumn: ColumnNode) => void\n}\n\nexport function useColResize(options: Options): UseColResizeReturned {\n const { scrollRef, leafColumns } = options\n\n const showResizeLine = shallowRef(false)\n\n const resizeLineRef = shallowRef<HTMLElement>()\n const colgroupRef = shallowRef<HTMLElement>()\n const headerRef = shallowRef<HTMLElement>()\n\n /**\n * 修正列样式\n */\n function correctColumnStyle() {\n // 获取表格容器宽度\n const containerWidth = scrollRef.value?.containerRef?.clientWidth\n\n if (!containerWidth || !leafColumns.value.length) return\n\n allocateLeafColumnWidths(leafColumns.value, containerWidth)\n\n const fixedLeft = leafColumns.value.filter((column) => column.fixed === 'left')\n const fixedRight = leafColumns.value.filter((column) => column.fixed === 'right')\n\n fixedLeft.reduce((acc, cur) => {\n cur.style.left = acc\n return acc + (cur.width ?? cur.minWidth!)\n }, 0)\n\n fixedRight.reduceRight((acc, cur) => {\n cur.style.right = acc\n return acc + (cur.width ?? cur.minWidth!)\n }, 0)\n }\n\n watchEffect(correctColumnStyle)\n\n // 容器宽度变化(窗口缩放、父布局变化、纵向滚动条出现/消失等)时重新分配列宽,\n // 直到所有未显式设宽的列回落到 minWidth。计算开销较大,用防抖合并连续的 resize 触发。\n const debouncedCorrectColumnStyle = debounce(correctColumnStyle)\n const containerRef = computed(() => scrollRef.value?.containerRef ?? null)\n useResizeObserver({ targets: containerRef, onResize: debouncedCorrectColumnStyle })\n\n function updateResizeLine(transformX: number): void {\n if (!resizeLineRef.value) return\n resizeLineRef.value.style.transform = `translateX(${transformX}px)`\n }\n\n // 表格相对于页面左边缘的偏移量,\n // 此处作为缓存以减少频繁地调用 getBoundingClientRect 方法\n let containerLeft = 0\n let originX = 0\n let originWidth = 0\n let currentResizeColumn: ColumnNode | null = null\n\n function handleResizeMousedown(e: MouseEvent, resizeColumn: ColumnNode): void {\n const tableEl = scrollRef.value?.el\n if (!tableEl) return\n\n showResizeLine.value = true\n currentResizeColumn = resizeColumn\n originX = e.pageX\n originWidth = resizeColumn.width ?? 0\n\n const { left } = tableEl.getBoundingClientRect()\n containerLeft = left\n\n nextTick(() => {\n updateResizeLine(e.pageX - left)\n })\n\n document.addEventListener('mousemove', handleResizeMousemove)\n document.addEventListener('mouseup', handleResizeMouseup)\n }\n\n function handleResizeMousemove(e: MouseEvent): void {\n updateResizeLine(e.pageX - containerLeft)\n }\n\n function handleResizeMouseup(e: MouseEvent): void {\n currentResizeColumn!.width = Math.max(\n originWidth + e.pageX - originX,\n currentResizeColumn!.minWidth!\n )\n currentResizeColumn!.explicitWidth = true\n\n showResizeLine.value = false\n currentResizeColumn = null\n correctColumnStyle()\n\n document.removeEventListener('mouseup', handleResizeMouseup)\n document.removeEventListener('mousemove', handleResizeMousemove)\n }\n\n provide(TableResizeKey, { handleResizeMousedown, headerRef })\n\n return { resizeLineRef, colgroupRef, showResizeLine, handleResizeMousedown }\n}\n"],"mappings":";;;;;;AAqBA,SAAgB,aAAa,SAAwC;CACnE,MAAM,EAAE,WAAW,gBAAgB;CAEnC,MAAM,iBAAiB,WAAW,KAAK;CAEvC,MAAM,gBAAgB,WAAwB;CAC9C,MAAM,cAAc,WAAwB;CAC5C,MAAM,YAAY,WAAwB;;;;CAK1C,SAAS,qBAAqB;EAE5B,MAAM,iBAAiB,UAAU,OAAO,cAAc;EAEtD,IAAI,CAAC,kBAAkB,CAAC,YAAY,MAAM,QAAQ;EAElD,yBAAyB,YAAY,OAAO,cAAc;EAE1D,MAAM,YAAY,YAAY,MAAM,QAAQ,WAAW,OAAO,UAAU,MAAM;EAC9E,MAAM,aAAa,YAAY,MAAM,QAAQ,WAAW,OAAO,UAAU,OAAO;EAEhF,UAAU,QAAQ,KAAK,QAAQ;GAC7B,IAAI,MAAM,OAAO;GACjB,OAAO,OAAO,IAAI,SAAS,IAAI;EACjC,GAAG,CAAC;EAEJ,WAAW,aAAa,KAAK,QAAQ;GACnC,IAAI,MAAM,QAAQ;GAClB,OAAO,OAAO,IAAI,SAAS,IAAI;EACjC,GAAG,CAAC;CACN;CAEA,YAAY,kBAAkB;CAI9B,MAAM,8BAA8B,SAAS,kBAAkB;CAC/D,MAAM,eAAe,eAAe,UAAU,OAAO,gBAAgB,IAAI;CACzE,kBAAkB;EAAE,SAAS;EAAc,UAAU;CAA4B,CAAC;CAElF,SAAS,iBAAiB,YAA0B;EAClD,IAAI,CAAC,cAAc,OAAO;EAC1B,cAAc,MAAM,MAAM,YAAY,cAAc,WAAW;CACjE;CAIA,IAAI,gBAAgB;CACpB,IAAI,UAAU;CACd,IAAI,cAAc;CAClB,IAAI,sBAAyC;CAE7C,SAAS,sBAAsB,GAAe,cAAgC;EAC5E,MAAM,UAAU,UAAU,OAAO;EACjC,IAAI,CAAC,SAAS;EAEd,eAAe,QAAQ;EACvB,sBAAsB;EACtB,UAAU,EAAE;EACZ,cAAc,aAAa,SAAS;EAEpC,MAAM,EAAE,SAAS,QAAQ,sBAAsB;EAC/C,gBAAgB;EAEhB,eAAe;GACb,iBAAiB,EAAE,QAAQ,IAAI;EACjC,CAAC;EAED,SAAS,iBAAiB,aAAa,qBAAqB;EAC5D,SAAS,iBAAiB,WAAW,mBAAmB;CAC1D;CAEA,SAAS,sBAAsB,GAAqB;EAClD,iBAAiB,EAAE,QAAQ,aAAa;CAC1C;CAEA,SAAS,oBAAoB,GAAqB;EAChD,oBAAqB,QAAQ,KAAK,IAChC,cAAc,EAAE,QAAQ,SACxB,oBAAqB,QACvB;EACA,oBAAqB,gBAAgB;EAErC,eAAe,QAAQ;EACvB,sBAAsB;EACtB,mBAAmB;EAEnB,SAAS,oBAAoB,WAAW,mBAAmB;EAC3D,SAAS,oBAAoB,aAAa,qBAAqB;CACjE;CAEA,QAAQ,gBAAgB;EAAE;EAAuB;CAAU,CAAC;CAE5D,OAAO;EAAE;EAAe;EAAa;EAAgB;CAAsB;AAC7E"}
@@ -43,7 +43,7 @@ interface CollapseItemProps {
43
43
  * @default false
44
44
  */
45
45
  modelValue?: boolean;
46
- /** 标题文本(也可使用 #title 插槽) */
46
+ /** 标题文本(也可通过 #header 插槽自定义整个头部) */
47
47
  title?: string;
48
48
  /** 是否禁用 */
49
49
  disabled?: boolean;
@@ -52,6 +52,12 @@ interface CollapseItemProps {
52
52
  * 在 `UCollapse` 内由父组件 `expandIcon` 统一管理。
53
53
  */
54
54
  expandIcon?: Component;
55
+ /**
56
+ * 折叠动画结束后卸载内容 DOM(展开时重新挂载),减少长列表的内存与渲染成本。
57
+ * 注意:卸载会丢失内容区的本地组件状态。
58
+ * @default false
59
+ */
60
+ destroyOnCollapse?: boolean;
55
61
  }
56
62
  interface CollapseItemEmits {
57
63
  (e: 'update:modelValue', value: boolean): void;
@@ -37,9 +37,24 @@ interface LayoutProps {
37
37
  * @description 注意:当为true时,gap固定且需要有一项宽度为固定像素才能够拖拽
38
38
  */
39
39
  resizable?: boolean;
40
+ /**
41
+ * 每列的最小宽度(px),按列索引与 cols 对应,仅 resizable 拖拽时生效。
42
+ * 未指定的列不限制(可被压至 0)。
43
+ * @example
44
+ * ```ts
45
+ * // 三栏拖拽时,左栏不小于 120px,右栏不小于 200px
46
+ * :cols="['1fr', '2fr', '300px']" :col-min-sizes="[120, undefined, 200]"
47
+ * ```
48
+ */
49
+ colMinSizes?: (number | undefined)[];
40
50
  }
41
51
  /** 布局组件定义的事件 */
42
- interface LayoutEmits {}
52
+ interface LayoutEmits {
53
+ /** 开始拖拽调节某条间隔(index 为间隔左侧列的索引) */
54
+ (e: 'resize-start', index: number): void;
55
+ /** 拖拽调节结束 */
56
+ (e: 'resize-end', index: number): void;
57
+ }
43
58
  /** 布局组件暴露的属性和方法(组件内部使用) */
44
59
  interface _LayoutExposed {}
45
60
  /** 布局组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -71,12 +71,12 @@
71
71
  "peerDependencies": {
72
72
  "@cat-kit/core": ">=1.1.8",
73
73
  "@cat-kit/fe": ">=1.1.8",
74
- "@veltra/compositions": "^1.5.0",
75
- "@veltra/directives": "^1.5.0",
74
+ "@veltra/compositions": "^1.6.0",
75
+ "@veltra/directives": "^1.6.0",
76
76
  "@veltra/icons": "^1.5.0",
77
- "@veltra/sheet-core": "^2.1.1",
78
- "@veltra/styles": "^1.5.0",
79
- "@veltra/utils": "^1.5.0",
77
+ "@veltra/sheet-core": "^2.3.0",
78
+ "@veltra/styles": "^1.6.0",
79
+ "@veltra/utils": "^1.6.0",
80
80
  "vue": ">=3.5.41"
81
81
  },
82
82
  "peerDependenciesMeta": {
@@ -65,8 +65,8 @@ const classList = computed(() => {
65
65
  })
66
66
 
67
67
  const ripple = computed(() => {
68
- if (props.disabled || props.loading) return false
69
- if ((props.plain || props.text) && props.type) {
68
+ if (props.disabled || props.loading || props.text) return false
69
+ if (props.plain && props.type) {
70
70
  return bem.is(`ripple-${props.type}`)
71
71
  }
72
72
  return true
@@ -47,17 +47,8 @@ $root-name: button;
47
47
  }
48
48
 
49
49
  @include m.is(plain) {
50
- background-color: fn.use-var(bg-color, top);
51
- border-color: fn.use-var(color, default, 'dark-1');
50
+ border-color: transparent;
52
51
  box-shadow: none;
53
-
54
- &:hover {
55
- border-color: fn.use-var(color, default, 'dark-1');
56
- }
57
-
58
- &.is-disabled {
59
- color: fn.use-var(text-color, second);
60
- }
61
52
  }
62
53
 
63
54
  // 尺寸
@@ -122,12 +113,9 @@ $root-name: button;
122
113
 
123
114
  @include m.is(text) {
124
115
  color: fn.use-var(color, $type);
116
+ border-color: transparent;
125
117
  box-shadow: none;
126
118
 
127
- &:hover {
128
- box-shadow: 0px 0px 0px 1px fn.use-var(color, $type);
129
- }
130
-
131
119
  @include m.is-not(disabled, loading) {
132
120
  &:hover,
133
121
  &:focus {
@@ -140,39 +128,30 @@ $root-name: button;
140
128
  }
141
129
 
142
130
  @include m.is(disabled, loading) {
131
+ border-color: transparent;
143
132
  color: fn.use-var(color, $type, 'light-5');
144
133
  }
145
134
  }
146
135
 
147
136
  @include m.is(plain) {
137
+ border-color: transparent;
148
138
  color: fn.component-var(button, #{$type}-plain-color, fn.use-var(color, $type));
149
139
  background-color: fn.component-var(
150
140
  button,
151
141
  #{$type}-plain-bg,
152
142
  fn.use-var(color, $type, light-9)
153
143
  );
154
- border-color: fn.component-var(
155
- button,
156
- #{$type}-plain-border,
157
- fn.use-var(color, $type, light-5)
158
- );
159
- box-shadow: none;
160
144
 
161
145
  @include m.is-not(disabled, loading) {
162
146
  &:hover {
163
- color: fn.use-var(color, $type);
164
- border-color: fn.use-var(color, $type);
165
- background-color: fn.use-var(color, $type, light-9);
147
+ background-color: fn.use-var(color, $type, light-7);
166
148
  }
167
149
  }
168
150
 
169
- &:active {
170
- box-shadow: none;
171
- }
172
-
173
- &.is-disabled {
151
+ @include m.is(disabled, loading) {
152
+ border-color: transparent;
174
153
  color: fn.use-var(color, $type, light-5);
175
- border-color: fn.use-var(color, $type, light-8);
154
+ background-color: fn.use-var(color, $type, light-9);
176
155
  }
177
156
  }
178
157
  }
@@ -353,3 +353,62 @@ describe('CollapseItem standalone', () => {
353
353
  }
354
354
  })
355
355
  })
356
+
357
+ describe('CollapseItem destroyOnCollapse', () => {
358
+ it('keeps content mounted by default even when collapsed', async () => {
359
+ const { host, unmount } = mountStandaloneItem({ title: 'Standalone' })
360
+
361
+ try {
362
+ await nextTick()
363
+ expect(host.querySelector('.u-collapse__item.is-active')).toBeFalsy()
364
+ expect(host.querySelector('.u-collapse__content')?.textContent).toBe('Standalone content')
365
+ } finally {
366
+ unmount()
367
+ }
368
+ })
369
+
370
+ it('does not mount content when initially collapsed', async () => {
371
+ const { host, unmount } = mountStandaloneItem({ title: 'Standalone', destroyOnCollapse: true })
372
+
373
+ try {
374
+ await nextTick()
375
+ expect(host.querySelector('.u-collapse__item.is-active')).toBeFalsy()
376
+ // 内容未挂载,但包装容器仍在(承载高度动画与 aria-hidden)
377
+ expect(host.querySelector('.u-collapse__content')).toBeFalsy()
378
+ expect(host.querySelector('.u-collapse__content-wrapper')).toBeTruthy()
379
+ } finally {
380
+ unmount()
381
+ }
382
+ })
383
+
384
+ it('unmounts content after collapse and remounts on expand', async () => {
385
+ const { host, model, unmount } = mountStandaloneItem({
386
+ title: 'Standalone',
387
+ modelValue: true,
388
+ destroyOnCollapse: true
389
+ })
390
+
391
+ try {
392
+ await nextTick()
393
+ expect(host.querySelector('.u-collapse__content')?.textContent).toBe('Standalone content')
394
+
395
+ const header = host.querySelector('.u-collapse__header') as HTMLElement
396
+ header.click()
397
+ await nextTick()
398
+
399
+ expect(model.value).toBe(false)
400
+ // happy-dom 无真实布局,收起动画直接落定,内容随即卸载
401
+ await nextTick()
402
+ expect(host.querySelector('.u-collapse__content')).toBeFalsy()
403
+
404
+ header.click()
405
+ await nextTick()
406
+ await nextTick()
407
+
408
+ expect(model.value).toBe(true)
409
+ expect(host.querySelector('.u-collapse__content')?.textContent).toBe('Standalone content')
410
+ } finally {
411
+ unmount()
412
+ }
413
+ })
414
+ })
@@ -10,18 +10,16 @@
10
10
  @keydown.enter.prevent="handleClick"
11
11
  @keydown.space.prevent="handleClick"
12
12
  >
13
- <span :class="cls.e('title')">
14
- <slot name="title">{{ title }}</slot>
15
- </span>
13
+ <slot name="header" :is-active="isActive">
14
+ <span :class="cls.e('title')">{{ title }}</span>
16
15
 
17
- <span :class="cls.e('icon')">
18
- <slot name="icon" :is-active="isActive">
16
+ <span :class="cls.e('icon')">
19
17
  <UIcon><component :is="iconComponent" /></UIcon>
20
- </slot>
21
- </span>
18
+ </span>
19
+ </slot>
22
20
  </div>
23
21
  <div ref="wrapperEl" :class="cls.e('content-wrapper')" :aria-hidden="!isActive">
24
- <div :class="cls.e('content')">
22
+ <div v-if="contentMounted" :class="cls.e('content')">
25
23
  <slot />
26
24
  </div>
27
25
  </div>
@@ -32,7 +30,7 @@
32
30
  import { useModel } from '@veltra/compositions'
33
31
  import { ArrowDown } from '@veltra/icons/normal'
34
32
  import { bem, ExpandTransition } from '@veltra/utils'
35
- import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'
33
+ import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
36
34
 
37
35
  import type { CollapseItemEmits, CollapseItemProps } from '../../types'
38
36
  import { UIcon } from '../icon'
@@ -43,6 +41,13 @@ defineOptions({ name: 'UCollapseItem' })
43
41
  const props = withDefaults(defineProps<CollapseItemProps>(), { modelValue: false })
44
42
  const emit = defineEmits<CollapseItemEmits>()
45
43
 
44
+ defineSlots<{
45
+ /** 自定义整个头部,作用域参数 `isActive` 指示展开状态;缺省渲染 title + 展开图标 */
46
+ header?: (scope: { isActive: boolean }) => any
47
+ /** 折叠面板内容 */
48
+ default?: () => any
49
+ }>()
50
+
46
51
  const context = inject(CollapseDIKey, undefined)
47
52
 
48
53
  const cls = context?.cls ?? bem('collapse')
@@ -94,6 +99,9 @@ const handleClick = () => {
94
99
 
95
100
  const wrapperEl = ref<HTMLElement>()
96
101
 
102
+ /** 内容是否挂载:destroyOnCollapse 时折叠态卸载内容 DOM,展开前重新挂载 */
103
+ const contentMounted = ref(isActive.value || !props.destroyOnCollapse)
104
+
97
105
  onMounted(() => {
98
106
  if (context && props.value !== undefined) {
99
107
  context.register(props.value)
@@ -102,9 +110,22 @@ onMounted(() => {
102
110
  expandTransition.setExpanded(wrapperEl.value, isActive.value)
103
111
  })
104
112
 
105
- watch(isActive, (active) => {
113
+ watch(isActive, async (active) => {
114
+ if (active) {
115
+ // 展开前先挂载内容,待 DOM 更新后再测量高度驱动展开动画
116
+ if (!contentMounted.value) {
117
+ contentMounted.value = true
118
+ await nextTick()
119
+ }
120
+ if (!wrapperEl.value || !isActive.value) return
121
+ expandTransition.expand(wrapperEl.value)
122
+ return
123
+ }
106
124
  if (!wrapperEl.value) return
107
- active ? expandTransition.expand(wrapperEl.value) : expandTransition.collapse(wrapperEl.value)
125
+ // 收起动画落定后再卸载内容 DOM;期间被重新展开则不卸载
126
+ expandTransition.collapse(wrapperEl.value, () => {
127
+ if (!isActive.value && props.destroyOnCollapse) contentMounted.value = false
128
+ })
108
129
  })
109
130
 
110
131
  onBeforeUnmount(() => {
@@ -76,10 +76,14 @@ $root-name: dialog;
76
76
  }
77
77
 
78
78
  @include m.e(body) {
79
- padding: 8px;
80
79
  background-color: fn.use-var(bg-color, top);
81
80
  backdrop-filter: fn.use-var(bg-filter);
82
81
  flex-grow: 1;
82
+
83
+ // padding 加在滚动内容而非 body 本身:u-scroll 的 overflow 会裁切子元素 box-shadow
84
+ @include m.bem(scroll, content) {
85
+ padding: 8px;
86
+ }
83
87
  }
84
88
 
85
89
  @include m.e(footer) {
@@ -11,6 +11,7 @@
11
11
  direction="vertical"
12
12
  @resize="handleResize($event, index)"
13
13
  @resize-start="handleStartResize(index)"
14
+ @resize-end="emit('resize-end', index)"
14
15
  >
15
16
  </ULayoutResizer>
16
17
 
@@ -37,7 +38,7 @@ import {
37
38
  nextTick
38
39
  } from 'vue'
39
40
 
40
- import type { LayoutProps } from '../../types'
41
+ import type { LayoutEmits, LayoutProps } from '../../types'
41
42
  import { LayoutDIKey } from './di'
42
43
  import ULayoutResizer from './layout-resizer.vue'
43
44
 
@@ -45,6 +46,8 @@ defineOptions({ name: 'ULayout' })
45
46
 
46
47
  const props = withDefaults(defineProps<LayoutProps>(), { tag: 'div' })
47
48
 
49
+ const emit = defineEmits<LayoutEmits>()
50
+
48
51
  const cls = bem('layout')
49
52
 
50
53
  const templateCols = ref<string[]>([])
@@ -87,20 +90,25 @@ function getResizeOffsets() {
87
90
 
88
91
  watch([() => props.resizable, containerRef, () => props.cols], () => {
89
92
  nextTick(() => {
90
- getResizeOffsets()
93
+ syncResizers()
91
94
  })
92
95
  })
93
96
 
94
97
  useResizeObserver({
95
98
  targets: containerRef,
96
99
  onResize() {
97
- getResizeOffsets()
98
- resizerRefs.value.forEach((r, i) => {
99
- r.update(resizerOffsets.value![i]!)
100
- })
100
+ syncResizers()
101
101
  }
102
102
  })
103
103
 
104
+ /** 重新测量各列边界并同步所有拖拽手柄位置(容器尺寸或 cols 程序化变化后调用) */
105
+ function syncResizers() {
106
+ getResizeOffsets()
107
+ resizerRefs.value.forEach((r, i) => {
108
+ r.update(resizerOffsets.value![i]!)
109
+ })
110
+ }
111
+
104
112
  // const horizontalResizerList = []
105
113
 
106
114
  // const resizing = shallowRef(false)
@@ -117,11 +125,14 @@ const handleStartResize = (index: number) => {
117
125
  const nextDom = container.children[index + 1] as HTMLElement | undefined
118
126
  prevSize = prevDom ? prevDom.getBoundingClientRect().width : 0
119
127
  nextSize = nextDom ? nextDom.getBoundingClientRect().width : 0
128
+ emit('resize-start', index)
120
129
  }
121
130
  const handleResize = (offset: number, index: number) => {
122
- // 钳位到 [-prevSize, nextSize],避免轨道尺寸出现负值——
131
+ // 钳位到 [colMin-prevSize, nextSize-colMin],避免轨道尺寸出现负值或小于列最小宽度——
123
132
  // 一旦 grid-template-columns 中带负值,整条声明会被浏览器丢弃。
124
- const clamped = Math.max(-prevSize, Math.min(nextSize, offset))
133
+ const minPrev = props.colMinSizes?.[index] ?? 0
134
+ const minNext = props.colMinSizes?.[index + 1] ?? 0
135
+ const clamped = Math.max(minPrev - prevSize, Math.min(nextSize - minNext, offset))
125
136
  templateCols.value[index] = `${prevSize + clamped}px`
126
137
  templateCols.value[index + 1] = `${nextSize - clamped}px`
127
138
  }
@@ -1,4 +1,6 @@
1
- import { nextTick, provide, shallowRef, watchEffect, type ShallowRef } from 'vue'
1
+ import { debounce } from '@cat-kit/core'
2
+ import { useResizeObserver } from '@veltra/compositions'
3
+ import { computed, nextTick, provide, shallowRef, watchEffect, type ShallowRef } from 'vue'
2
4
 
3
5
  import type { ScrollExposed } from '../../types'
4
6
  import { allocateLeafColumnWidths } from './allocate-column-widths'
@@ -53,6 +55,12 @@ export function useColResize(options: Options): UseColResizeReturned {
53
55
 
54
56
  watchEffect(correctColumnStyle)
55
57
 
58
+ // 容器宽度变化(窗口缩放、父布局变化、纵向滚动条出现/消失等)时重新分配列宽,
59
+ // 直到所有未显式设宽的列回落到 minWidth。计算开销较大,用防抖合并连续的 resize 触发。
60
+ const debouncedCorrectColumnStyle = debounce(correctColumnStyle)
61
+ const containerRef = computed(() => scrollRef.value?.containerRef ?? null)
62
+ useResizeObserver({ targets: containerRef, onResize: debouncedCorrectColumnStyle })
63
+
56
64
  function updateResizeLine(transformX: number): void {
57
65
  if (!resizeLineRef.value) return
58
66
  resizeLineRef.value.style.transform = `translateX(${transformX}px)`
@@ -52,7 +52,7 @@ export interface CollapseItemProps {
52
52
  */
53
53
  modelValue?: boolean
54
54
 
55
- /** 标题文本(也可使用 #title 插槽) */
55
+ /** 标题文本(也可通过 #header 插槽自定义整个头部) */
56
56
  title?: string
57
57
 
58
58
  /** 是否禁用 */
@@ -63,6 +63,13 @@ export interface CollapseItemProps {
63
63
  * 在 `UCollapse` 内由父组件 `expandIcon` 统一管理。
64
64
  */
65
65
  expandIcon?: Component
66
+
67
+ /**
68
+ * 折叠动画结束后卸载内容 DOM(展开时重新挂载),减少长列表的内存与渲染成本。
69
+ * 注意:卸载会丢失内容区的本地组件状态。
70
+ * @default false
71
+ */
72
+ destroyOnCollapse?: boolean
66
73
  }
67
74
 
68
75
  export interface CollapseItemEmits {
@@ -38,10 +38,25 @@ export interface LayoutProps {
38
38
  * @description 注意:当为true时,gap固定且需要有一项宽度为固定像素才能够拖拽
39
39
  */
40
40
  resizable?: boolean
41
+ /**
42
+ * 每列的最小宽度(px),按列索引与 cols 对应,仅 resizable 拖拽时生效。
43
+ * 未指定的列不限制(可被压至 0)。
44
+ * @example
45
+ * ```ts
46
+ * // 三栏拖拽时,左栏不小于 120px,右栏不小于 200px
47
+ * :cols="['1fr', '2fr', '300px']" :col-min-sizes="[120, undefined, 200]"
48
+ * ```
49
+ */
50
+ colMinSizes?: (number | undefined)[]
41
51
  }
42
52
 
43
53
  /** 布局组件定义的事件 */
44
- export interface LayoutEmits {}
54
+ export interface LayoutEmits {
55
+ /** 开始拖拽调节某条间隔(index 为间隔左侧列的索引) */
56
+ (e: 'resize-start', index: number): void
57
+ /** 拖拽调节结束 */
58
+ (e: 'resize-end', index: number): void
59
+ }
45
60
 
46
61
  /** 布局组件暴露的属性和方法(组件内部使用) */
47
62
  export interface _LayoutExposed {}