@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.
- package/dist/components/button/button.js +2 -2
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/button/style2.css +85 -92
- package/dist/components/collapse/collapse-item.js +24 -7
- package/dist/components/collapse/collapse-item.js.map +1 -1
- package/dist/components/dialog/style2.css +3 -1
- package/dist/components/layout/layout.js +22 -10
- package/dist/components/layout/layout.js.map +1 -1
- package/dist/components/table/use-col-resize.js +9 -1
- package/dist/components/table/use-col-resize.js.map +1 -1
- package/dist/types/collapse.d.ts +7 -1
- package/dist/types/layout.d.ts +16 -1
- package/package.json +6 -6
- package/src/components/button/button.vue +2 -2
- package/src/components/button/style.scss +8 -29
- package/src/components/collapse/__test__/collapse.test.ts +59 -0
- package/src/components/collapse/collapse-item.vue +32 -11
- package/src/components/dialog/style.scss +5 -1
- package/src/components/layout/layout.vue +19 -8
- package/src/components/table/use-col-resize.ts +9 -1
- package/src/types/collapse.ts +8 -1
- package/src/types/layout.ts +16 -1
|
@@ -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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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")) },
|
|
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
|
-
})]
|
|
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
|
-
}, [
|
|
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')\"
|
|
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
|
-
|
|
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
|
-
|
|
58
|
+
syncResizers();
|
|
56
59
|
});
|
|
57
60
|
});
|
|
58
61
|
useResizeObserver({
|
|
59
62
|
targets: containerRef,
|
|
60
63
|
onResize() {
|
|
61
|
-
|
|
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
|
|
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
|
|
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":";;;;;;;;;;;;;;;;;;;EA8CA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,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,aAAa;GACf,CAAC;EACH,CAAC;EAED,kBAAkB;GAChB,SAAS;GACT,WAAW;IACT,aAAa;GACf;EACF,CAAC;;EAGD,SAAS,eAAe;GACtB,iBAAiB;GACjB,YAAY,MAAM,SAAS,GAAG,MAAM;IAClC,EAAE,OAAO,eAAe,MAAO,EAAG;GACpC,CAAC;EACH;EAMA,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;GAC7D,KAAK,gBAAgB,KAAK;EAC5B;EACA,MAAM,gBAAgB,QAAgB,UAAkB;GAGtD,MAAM,UAAU,MAAM,cAAc,UAAU;GAC9C,MAAM,UAAU,MAAM,cAAc,QAAQ,MAAM;GAClD,MAAM,UAAU,KAAK,IAAI,UAAU,UAAU,KAAK,IAAI,WAAW,SAAS,MAAM,CAAC;GACjF,aAAa,MAAM,SAAS,GAAG,WAAW,QAAQ;GAClD,aAAa,MAAM,QAAQ,KAAK,GAAG,WAAW,QAAQ;EACxD;EAEA,QAAQ,aAAa,EAAE,IAAI,CAAC;;GA1I1B,OAAA,UAAA,GAAA,YAsBY,wBAtBI,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,mBAUiB,UAAA,MAAA,WATW,eAAA,QAAlB,QAAQ,UAAK;MADvB,OAAA,UAAA,GAAA,YAUiB,aAAA;OARN;OACR,KAAK;;OACF,SAAA;OAAJ,KAAI;OACJ,WAAU;OACT,WAAM,WAAE,aAAa,QAAQ,KAAK;OAClC,gBAAY,WAAE,kBAAkB,KAAK;OACrC,cAAU,WAAE,KAAI,cAAe,KAAK;;;;;;;;KAIvC,mBAAA,QAAA;KACA,mBAAA,+HAAA"}
|
|
@@ -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":"
|
|
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"}
|
package/dist/types/collapse.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ interface CollapseItemProps {
|
|
|
43
43
|
* @default false
|
|
44
44
|
*/
|
|
45
45
|
modelValue?: boolean;
|
|
46
|
-
/**
|
|
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;
|
package/dist/types/layout.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
75
|
-
"@veltra/directives": "^1.
|
|
74
|
+
"@veltra/compositions": "^1.6.0",
|
|
75
|
+
"@veltra/directives": "^1.6.0",
|
|
76
76
|
"@veltra/icons": "^1.5.0",
|
|
77
|
-
"@veltra/sheet-core": "^2.
|
|
78
|
-
"@veltra/styles": "^1.
|
|
79
|
-
"@veltra/utils": "^1.
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
14
|
-
<
|
|
15
|
-
</span>
|
|
13
|
+
<slot name="header" :is-active="isActive">
|
|
14
|
+
<span :class="cls.e('title')">{{ title }}</span>
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
<slot name="icon" :is-active="isActive">
|
|
16
|
+
<span :class="cls.e('icon')">
|
|
19
17
|
<UIcon><component :is="iconComponent" /></UIcon>
|
|
20
|
-
</
|
|
21
|
-
</
|
|
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
|
-
|
|
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
|
-
|
|
93
|
+
syncResizers()
|
|
91
94
|
})
|
|
92
95
|
})
|
|
93
96
|
|
|
94
97
|
useResizeObserver({
|
|
95
98
|
targets: containerRef,
|
|
96
99
|
onResize() {
|
|
97
|
-
|
|
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
|
|
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 {
|
|
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)`
|
package/src/types/collapse.ts
CHANGED
|
@@ -52,7 +52,7 @@ export interface CollapseItemProps {
|
|
|
52
52
|
*/
|
|
53
53
|
modelValue?: boolean
|
|
54
54
|
|
|
55
|
-
/**
|
|
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 {
|
package/src/types/layout.ts
CHANGED
|
@@ -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 {}
|