tudou-waterfall 0.1.4 → 0.1.6
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/index.cjs +37 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +19 -2
- package/dist/index.mjs +37 -24
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +3 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,10 +5,17 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
5
5
|
__name: "Waterfall",
|
|
6
6
|
props: {
|
|
7
7
|
items: {},
|
|
8
|
-
cols: {},
|
|
8
|
+
cols: { default: 0 },
|
|
9
9
|
gap: { default: 15 },
|
|
10
10
|
estimatedHeight: { default: 220 },
|
|
11
|
-
overscan: { default: 600 }
|
|
11
|
+
overscan: { default: 600 },
|
|
12
|
+
breakpoints: { default: () => [
|
|
13
|
+
{ minWidth: 1400, cols: 9 },
|
|
14
|
+
{ minWidth: 1200, cols: 8 },
|
|
15
|
+
{ minWidth: 900, cols: 7 },
|
|
16
|
+
{ minWidth: 700, cols: 6 },
|
|
17
|
+
{ minWidth: 500, cols: 5 }
|
|
18
|
+
] }
|
|
12
19
|
},
|
|
13
20
|
emits: ["reflow"],
|
|
14
21
|
setup(__props, { emit: __emit }) {
|
|
@@ -20,7 +27,13 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
20
27
|
const colPositions = vue.ref([]);
|
|
21
28
|
const colTotalHeights = vue.ref([]);
|
|
22
29
|
const measuredHeights = /* @__PURE__ */ new Map();
|
|
23
|
-
const getColCount = () =>
|
|
30
|
+
const getColCount = () => {
|
|
31
|
+
var _a, _b;
|
|
32
|
+
if (props.cols > 0) return props.cols;
|
|
33
|
+
const width = ((_a = containerRef.value) == null ? void 0 : _a.clientWidth) ?? window.innerWidth;
|
|
34
|
+
const sorted = [...props.breakpoints].sort((a, b) => b.minWidth - a.minWidth);
|
|
35
|
+
return ((_b = sorted.find((bp) => width >= bp.minWidth)) == null ? void 0 : _b.cols) ?? 2;
|
|
36
|
+
};
|
|
24
37
|
const reflow = async () => {
|
|
25
38
|
await vue.nextTick();
|
|
26
39
|
const count = getColCount();
|
|
@@ -53,6 +66,23 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
53
66
|
break;
|
|
54
67
|
}
|
|
55
68
|
};
|
|
69
|
+
const itemElMap = /* @__PURE__ */ new WeakMap();
|
|
70
|
+
const itemRo = new ResizeObserver((entries) => {
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const globalIdx = itemElMap.get(entry.target);
|
|
73
|
+
if (globalIdx === void 0) continue;
|
|
74
|
+
const h = entry.contentRect.height;
|
|
75
|
+
if (h > 0) patchHeight(globalIdx, h);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const observeItem = (el, globalIdx) => {
|
|
79
|
+
itemElMap.set(el, globalIdx);
|
|
80
|
+
itemRo.observe(el);
|
|
81
|
+
};
|
|
82
|
+
const unobserveItem = (el) => {
|
|
83
|
+
itemRo.unobserve(el);
|
|
84
|
+
itemElMap.delete(el);
|
|
85
|
+
};
|
|
56
86
|
const visibleItems = vue.computed(() => {
|
|
57
87
|
const top = scrollTop.value - props.overscan;
|
|
58
88
|
const bottom = scrollTop.value + viewportHeight.value + props.overscan;
|
|
@@ -75,23 +105,6 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
75
105
|
const containerOffset = containerRef.value ? containerRef.value.offsetTop : 0;
|
|
76
106
|
scrollTop.value = Math.max(0, st - containerOffset);
|
|
77
107
|
};
|
|
78
|
-
const itemElMap = /* @__PURE__ */ new WeakMap();
|
|
79
|
-
const itemRo = new ResizeObserver((entries) => {
|
|
80
|
-
for (const entry of entries) {
|
|
81
|
-
const globalIdx = itemElMap.get(entry.target);
|
|
82
|
-
if (globalIdx === void 0) continue;
|
|
83
|
-
const h = entry.contentRect.height;
|
|
84
|
-
if (h > 0) patchHeight(globalIdx, h);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
const observeItem = (el, globalIdx) => {
|
|
88
|
-
itemElMap.set(el, globalIdx);
|
|
89
|
-
itemRo.observe(el);
|
|
90
|
-
};
|
|
91
|
-
const unobserveItem = (el) => {
|
|
92
|
-
itemRo.unobserve(el);
|
|
93
|
-
itemElMap.delete(el);
|
|
94
|
-
};
|
|
95
108
|
let containerRo = null;
|
|
96
109
|
vue.onMounted(async () => {
|
|
97
110
|
await vue.nextTick();
|
|
@@ -111,13 +124,13 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
111
124
|
containerRo == null ? void 0 : containerRo.disconnect();
|
|
112
125
|
itemRo.disconnect();
|
|
113
126
|
});
|
|
114
|
-
vue.watch(() => props.cols, () => {
|
|
115
|
-
reflow();
|
|
116
|
-
});
|
|
117
127
|
vue.watch(() => props.items.length, (newLen, oldLen) => {
|
|
118
128
|
if (newLen < oldLen) measuredHeights.clear();
|
|
119
129
|
reflow();
|
|
120
130
|
});
|
|
131
|
+
vue.watch(() => props.cols, () => {
|
|
132
|
+
reflow();
|
|
133
|
+
});
|
|
121
134
|
return (_ctx, _cache) => {
|
|
122
135
|
return vue.openBlock(), vue.createElementBlock("div", {
|
|
123
136
|
ref_key: "containerRef",
|
|
@@ -158,7 +171,7 @@ const _export_sfc = (sfc, props) => {
|
|
|
158
171
|
}
|
|
159
172
|
return target;
|
|
160
173
|
};
|
|
161
|
-
const Waterfall = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
174
|
+
const Waterfall = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4ad3e2ed"]]);
|
|
162
175
|
function install(app) {
|
|
163
176
|
app.component("TdWaterfall", Waterfall);
|
|
164
177
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/components/Waterfall.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'\n\nexport interface WaterfallProps {\n items: any[]\n /** Number of columns. Required when not using breakpoints externally. */\n cols: number\n gap?: number\n estimatedHeight?: number\n overscan?: number\n}\n\nconst props = withDefaults(defineProps<WaterfallProps>(), {\n gap: 15,\n estimatedHeight: 220,\n overscan: 600,\n})\n\nconst emit = defineEmits<{ (e: 'reflow'): void }>()\n\n// ─── types ────────────────────────────────────────────────────────────────────\n\ninterface PositionedItem {\n item: any\n globalIdx: number\n top: number\n height: number\n}\n\n// ─── state ────────────────────────────────────────────────────────────────────\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst scrollTop = ref(0)\nconst viewportHeight = ref(0)\n\n// colPositions[colIdx] = sorted array of positioned items\nconst colPositions = ref<PositionedItem[][]>([])\n// colTotalHeight[colIdx] = total height of that column (for the spacer div)\nconst colTotalHeights = ref<number[]>([])\n\n// globalIdx -> measured height (updated after image load)\nconst measuredHeights = new Map<number, number>()\n\n// ─── column count ─────────────────────────────────────────────────────────────\n\nconst getColCount = () => Math.max(1, props.cols)\n\n// ─── full reflow: re-assign items to columns ──────────────────────────────────\n// Only called when items list changes or viewport width changes.\n// Does NOT re-assign on height updates to avoid item jumping between columns.\n\nconst reflow = async () => {\n await nextTick()\n const count = getColCount()\n const cols: PositionedItem[][] = Array.from({ length: count }, () => [])\n const colH = new Array(count).fill(0)\n\n props.items.forEach((item, i) => {\n const minIdx = colH.indexOf(Math.min(...colH))\n const h = measuredHeights.get(i) ?? props.estimatedHeight\n cols[minIdx].push({ item, globalIdx: i, top: colH[minIdx], height: h })\n colH[minIdx] += h + props.gap\n })\n\n colPositions.value = cols\n colTotalHeights.value = colH\n emit('reflow')\n}\n\n// ─── patch: update one item's height in-place, shift subsequent items ─────────\n// Called after an image loads. Avoids full reflow to prevent column reassignment.\n\nconst patchHeight = (globalIdx: number, newHeight: number) => {\n const old = measuredHeights.get(globalIdx) ?? props.estimatedHeight\n if (Math.abs(old - newHeight) < 2) return\n measuredHeights.set(globalIdx, newHeight)\n\n for (let ci = 0; ci < colPositions.value.length; ci++) {\n const col = colPositions.value[ci]\n const pos = col.findIndex(p => p.globalIdx === globalIdx)\n if (pos === -1) continue\n\n const delta = newHeight - col[pos].height\n col[pos].height = newHeight\n\n for (let j = pos + 1; j < col.length; j++) {\n col[j] = { ...col[j], top: col[j].top + delta }\n }\n\n colTotalHeights.value[ci] += delta\n break\n }\n}\n\n// ─── virtual visibility ───────────────────────────────────────────────────────\n\nconst visibleItems = computed(() => {\n const top = scrollTop.value - props.overscan\n const bottom = scrollTop.value + viewportHeight.value + props.overscan\n return colPositions.value.map(col =>\n col.filter(p => p.top + p.height >= top && p.top <= bottom)\n )\n})\n\n// ─── scroll listener ──────────────────────────────────────────────────────────\n\nlet scrollEl: Element | null = null\n\nconst findScrollParent = (el: Element): Element => {\n let cur: Element | null = el.parentElement\n while (cur) {\n const { overflow, overflowY } = getComputedStyle(cur)\n if (/auto|scroll/.test(overflow + overflowY)) return cur\n cur = cur.parentElement\n }\n return document.documentElement\n}\n\nconst onScroll = (e: Event) => {\n const st = (e.target as Element).scrollTop\n const containerOffset = containerRef.value\n ? (containerRef.value as HTMLElement).offsetTop\n : 0\n scrollTop.value = Math.max(0, st - containerOffset)\n}\n\n// ─── item resize observer ─────────────────────────────────────────────────────\n// One shared RO watches every .wf-item element.\n// el -> globalIdx mapping is stored in a WeakMap so we never leak.\n\nconst itemElMap = new WeakMap<Element, number>()\n\nconst itemRo = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const globalIdx = itemElMap.get(entry.target)\n if (globalIdx === undefined) continue\n const h = entry.contentRect.height\n if (h > 0) patchHeight(globalIdx, h)\n }\n})\n\nconst observeItem = (el: Element, globalIdx: number) => {\n itemElMap.set(el, globalIdx)\n itemRo.observe(el)\n}\n\nconst unobserveItem = (el: Element) => {\n itemRo.unobserve(el)\n itemElMap.delete(el)\n}\n\n// ─── resize observer (container) ─────────────────────────────────────────────\n\nlet containerRo: ResizeObserver | null = null\n\n// ─── lifecycle ────────────────────────────────────────────────────────────────\n\nonMounted(async () => {\n await nextTick()\n if (!containerRef.value) return\n\n scrollEl = findScrollParent(containerRef.value)\n scrollEl.addEventListener('scroll', onScroll, { passive: true })\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n\n containerRo = new ResizeObserver(() => {\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n reflow()\n })\n containerRo.observe(containerRef.value)\n\n reflow()\n})\n\nonUnmounted(() => {\n scrollEl?.removeEventListener('scroll', onScroll)\n containerRo?.disconnect()\n itemRo.disconnect()\n})\n\nwatch(() => props.cols, () => {\n reflow()\n})\n\nwatch(() => props.items.length, (newLen, oldLen) => {\n if (newLen < oldLen) measuredHeights.clear()\n reflow()\n})\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"wf-root\" :style=\"{ gap: `${gap}px` }\">\n <div\n v-for=\"(_col, ci) in colPositions\"\n :key=\"ci\"\n class=\"wf-col\"\n :style=\"{ height: `${colTotalHeights[ci]}px` }\"\n >\n <div\n v-for=\"{ item, globalIdx, top } in visibleItems[ci]\"\n :key=\"globalIdx\"\n :ref=\"(el) => { if (el) observeItem(el as Element, globalIdx) }\"\n class=\"wf-item\"\n :style=\"{ transform: `translateY(${top}px)` }\"\n @vue:unmounted=\"({ el }: any) => unobserveItem(el)\"\n >\n <slot :item=\"item\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.wf-root {\n display: flex;\n align-items: flex-start;\n width: 100%;\n}\n\n.wf-col {\n flex: 1;\n position: relative;\n min-width: 0;\n}\n\n.wf-item {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n}\n</style>\n","import type { App, Plugin } from 'vue'\n\nimport Waterfall from './components/Waterfall.vue'\n\nexport { Waterfall }\nexport type { WaterfallProps } from './components/Waterfall.vue'\n\nfunction install(app: App): void {\n app.component('TdWaterfall', Waterfall)\n}\n\nconst TudouWaterfall: Plugin = {\n install,\n}\n\nexport default TudouWaterfall\nexport { TudouWaterfall }\n"],"names":["ref","nextTick","computed","onMounted","onUnmounted","watch","_createElementBlock","_openBlock","_Fragment","_renderList","_normalizeStyle","_renderSlot"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/components/Waterfall.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'\n\nexport interface WaterfallBreakpoint {\n minWidth: number\n cols: number\n}\n\nexport interface WaterfallProps {\n items: any[]\n cols?: number\n gap?: number\n estimatedHeight?: number\n overscan?: number\n breakpoints?: WaterfallBreakpoint[]\n}\n\nconst props = withDefaults(defineProps<WaterfallProps>(), {\n cols: 0,\n gap: 15,\n estimatedHeight: 220,\n overscan: 600,\n breakpoints: () => [\n { minWidth: 1400, cols: 9 },\n { minWidth: 1200, cols: 8 },\n { minWidth: 900, cols: 7 },\n { minWidth: 700, cols: 6 },\n { minWidth: 500, cols: 5 },\n ],\n})\n\nconst emit = defineEmits<{ (e: 'reflow'): void }>()\n\n// ─── types ────────────────────────────────────────────────────────────────────\n\ninterface PositionedItem {\n item: any\n globalIdx: number\n top: number\n height: number\n}\n\n// ─── state ────────────────────────────────────────────────────────────────────\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst scrollTop = ref(0)\nconst viewportHeight = ref(0)\n\nconst colPositions = ref<PositionedItem[][]>([])\nconst colTotalHeights = ref<number[]>([])\n\nconst measuredHeights = new Map<number, number>()\n\n// ─── column count ─────────────────────────────────────────────────────────────\n\nconst getColCount = () => {\n if (props.cols > 0) return props.cols\n const width = containerRef.value?.clientWidth ?? window.innerWidth\n const sorted = [...props.breakpoints].sort((a, b) => b.minWidth - a.minWidth)\n return sorted.find(bp => width >= bp.minWidth)?.cols ?? 2\n}\n\n// ─── full reflow ──────────────────────────────────────────────────────────────\n\nconst reflow = async () => {\n await nextTick()\n const count = getColCount()\n const cols: PositionedItem[][] = Array.from({ length: count }, () => [])\n const colH = new Array(count).fill(0)\n\n props.items.forEach((item, i) => {\n const minIdx = colH.indexOf(Math.min(...colH))\n const h = measuredHeights.get(i) ?? props.estimatedHeight\n cols[minIdx].push({ item, globalIdx: i, top: colH[minIdx], height: h })\n colH[minIdx] += h + props.gap\n })\n\n colPositions.value = cols\n colTotalHeights.value = colH\n emit('reflow')\n}\n\n// ─── patch height ─────────────────────────────────────────────────────────────\n\nconst patchHeight = (globalIdx: number, newHeight: number) => {\n const old = measuredHeights.get(globalIdx) ?? props.estimatedHeight\n if (Math.abs(old - newHeight) < 2) return\n measuredHeights.set(globalIdx, newHeight)\n\n for (let ci = 0; ci < colPositions.value.length; ci++) {\n const col = colPositions.value[ci]\n const pos = col.findIndex(p => p.globalIdx === globalIdx)\n if (pos === -1) continue\n\n const delta = newHeight - col[pos].height\n col[pos].height = newHeight\n\n for (let j = pos + 1; j < col.length; j++) {\n col[j] = { ...col[j], top: col[j].top + delta }\n }\n\n colTotalHeights.value[ci] += delta\n break\n }\n}\n\n// ─── auto height via ResizeObserver ──────────────────────────────────────────\n\nconst itemElMap = new WeakMap<Element, number>()\n\nconst itemRo = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const globalIdx = itemElMap.get(entry.target)\n if (globalIdx === undefined) continue\n const h = entry.contentRect.height\n if (h > 0) patchHeight(globalIdx, h)\n }\n})\n\nconst observeItem = (el: Element, globalIdx: number) => {\n itemElMap.set(el, globalIdx)\n itemRo.observe(el)\n}\n\nconst unobserveItem = (el: Element) => {\n itemRo.unobserve(el)\n itemElMap.delete(el)\n}\n\n// ─── virtual visibility ───────────────────────────────────────────────────────\n\nconst visibleItems = computed(() => {\n const top = scrollTop.value - props.overscan\n const bottom = scrollTop.value + viewportHeight.value + props.overscan\n return colPositions.value.map(col =>\n col.filter(p => p.top + p.height >= top && p.top <= bottom)\n )\n})\n\n// ─── scroll ───────────────────────────────────────────────────────────────────\n\nlet scrollEl: Element | null = null\n\nconst findScrollParent = (el: Element): Element => {\n let cur: Element | null = el.parentElement\n while (cur) {\n const { overflow, overflowY } = getComputedStyle(cur)\n if (/auto|scroll/.test(overflow + overflowY)) return cur\n cur = cur.parentElement\n }\n return document.documentElement\n}\n\nconst onScroll = (e: Event) => {\n const st = (e.target as Element).scrollTop\n const containerOffset = containerRef.value\n ? (containerRef.value as HTMLElement).offsetTop\n : 0\n scrollTop.value = Math.max(0, st - containerOffset)\n}\n\n// ─── lifecycle ────────────────────────────────────────────────────────────────\n\nlet containerRo: ResizeObserver | null = null\n\nonMounted(async () => {\n await nextTick()\n if (!containerRef.value) return\n\n scrollEl = findScrollParent(containerRef.value)\n scrollEl.addEventListener('scroll', onScroll, { passive: true })\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n\n containerRo = new ResizeObserver(() => {\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n reflow()\n })\n containerRo.observe(containerRef.value)\n\n reflow()\n})\n\nonUnmounted(() => {\n scrollEl?.removeEventListener('scroll', onScroll)\n containerRo?.disconnect()\n itemRo.disconnect()\n})\n\nwatch(() => props.items.length, (newLen, oldLen) => {\n if (newLen < oldLen) measuredHeights.clear()\n reflow()\n})\n\nwatch(() => props.cols, () => {\n reflow()\n})\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"wf-root\" :style=\"{ gap: `${gap}px` }\">\n <div\n v-for=\"(_col, ci) in colPositions\"\n :key=\"ci\"\n class=\"wf-col\"\n :style=\"{ height: `${colTotalHeights[ci]}px` }\"\n >\n <div\n v-for=\"{ item, globalIdx, top } in visibleItems[ci]\"\n :key=\"globalIdx\"\n :ref=\"(el) => { if (el) observeItem(el as Element, globalIdx) }\"\n class=\"wf-item\"\n :style=\"{ transform: `translateY(${top}px)` }\"\n @vue:unmounted=\"({ el }: any) => unobserveItem(el)\"\n >\n <slot :item=\"item\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.wf-root {\n display: flex;\n align-items: flex-start;\n width: 100%;\n}\n\n.wf-col {\n flex: 1;\n position: relative;\n min-width: 0;\n}\n\n.wf-item {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n}\n</style>\n","import type { App, Plugin } from 'vue'\n\nimport Waterfall from './components/Waterfall.vue'\n\nexport { Waterfall }\nexport type { WaterfallProps, WaterfallBreakpoint } from './components/Waterfall.vue'\n\nfunction install(app: App): void {\n app.component('TdWaterfall', Waterfall)\n}\n\nconst TudouWaterfall: Plugin = {\n install,\n}\n\nexport default TudouWaterfall\nexport { TudouWaterfall }\n"],"names":["ref","nextTick","computed","onMounted","onUnmounted","watch","_createElementBlock","_openBlock","_Fragment","_renderList","_normalizeStyle","_renderSlot"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,21 +7,33 @@ import { PropType } from 'vue';
|
|
|
7
7
|
import { PublicProps } from 'vue';
|
|
8
8
|
|
|
9
9
|
declare const __VLS_component: DefineComponent<ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<WaterfallProps>, {
|
|
10
|
+
cols: number;
|
|
10
11
|
gap: number;
|
|
11
12
|
estimatedHeight: number;
|
|
12
13
|
overscan: number;
|
|
14
|
+
breakpoints: () => {
|
|
15
|
+
minWidth: number;
|
|
16
|
+
cols: number;
|
|
17
|
+
}[];
|
|
13
18
|
}>>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
|
|
14
19
|
reflow: () => void;
|
|
15
20
|
}, string, PublicProps, Readonly<ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<WaterfallProps>, {
|
|
21
|
+
cols: number;
|
|
16
22
|
gap: number;
|
|
17
23
|
estimatedHeight: number;
|
|
18
24
|
overscan: number;
|
|
25
|
+
breakpoints: () => {
|
|
26
|
+
minWidth: number;
|
|
27
|
+
cols: number;
|
|
28
|
+
}[];
|
|
19
29
|
}>>> & Readonly<{
|
|
20
30
|
onReflow?: (() => any) | undefined;
|
|
21
31
|
}>, {
|
|
32
|
+
cols: number;
|
|
22
33
|
gap: number;
|
|
23
34
|
estimatedHeight: number;
|
|
24
35
|
overscan: number;
|
|
36
|
+
breakpoints: WaterfallBreakpoint[];
|
|
25
37
|
}, {}, {}, {}, string, ComponentProvideOptions, true, {}, any>;
|
|
26
38
|
|
|
27
39
|
declare type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
@@ -63,13 +75,18 @@ export default TudouWaterfall;
|
|
|
63
75
|
|
|
64
76
|
export declare const Waterfall: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
|
|
65
77
|
|
|
78
|
+
export declare interface WaterfallBreakpoint {
|
|
79
|
+
minWidth: number;
|
|
80
|
+
cols: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
export declare interface WaterfallProps {
|
|
67
84
|
items: any[];
|
|
68
|
-
|
|
69
|
-
cols: number;
|
|
85
|
+
cols?: number;
|
|
70
86
|
gap?: number;
|
|
71
87
|
estimatedHeight?: number;
|
|
72
88
|
overscan?: number;
|
|
89
|
+
breakpoints?: WaterfallBreakpoint[];
|
|
73
90
|
}
|
|
74
91
|
|
|
75
92
|
export { }
|
package/dist/index.mjs
CHANGED
|
@@ -3,10 +3,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
3
3
|
__name: "Waterfall",
|
|
4
4
|
props: {
|
|
5
5
|
items: {},
|
|
6
|
-
cols: {},
|
|
6
|
+
cols: { default: 0 },
|
|
7
7
|
gap: { default: 15 },
|
|
8
8
|
estimatedHeight: { default: 220 },
|
|
9
|
-
overscan: { default: 600 }
|
|
9
|
+
overscan: { default: 600 },
|
|
10
|
+
breakpoints: { default: () => [
|
|
11
|
+
{ minWidth: 1400, cols: 9 },
|
|
12
|
+
{ minWidth: 1200, cols: 8 },
|
|
13
|
+
{ minWidth: 900, cols: 7 },
|
|
14
|
+
{ minWidth: 700, cols: 6 },
|
|
15
|
+
{ minWidth: 500, cols: 5 }
|
|
16
|
+
] }
|
|
10
17
|
},
|
|
11
18
|
emits: ["reflow"],
|
|
12
19
|
setup(__props, { emit: __emit }) {
|
|
@@ -18,7 +25,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
18
25
|
const colPositions = ref([]);
|
|
19
26
|
const colTotalHeights = ref([]);
|
|
20
27
|
const measuredHeights = /* @__PURE__ */ new Map();
|
|
21
|
-
const getColCount = () =>
|
|
28
|
+
const getColCount = () => {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
if (props.cols > 0) return props.cols;
|
|
31
|
+
const width = ((_a = containerRef.value) == null ? void 0 : _a.clientWidth) ?? window.innerWidth;
|
|
32
|
+
const sorted = [...props.breakpoints].sort((a, b) => b.minWidth - a.minWidth);
|
|
33
|
+
return ((_b = sorted.find((bp) => width >= bp.minWidth)) == null ? void 0 : _b.cols) ?? 2;
|
|
34
|
+
};
|
|
22
35
|
const reflow = async () => {
|
|
23
36
|
await nextTick();
|
|
24
37
|
const count = getColCount();
|
|
@@ -51,6 +64,23 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
51
64
|
break;
|
|
52
65
|
}
|
|
53
66
|
};
|
|
67
|
+
const itemElMap = /* @__PURE__ */ new WeakMap();
|
|
68
|
+
const itemRo = new ResizeObserver((entries) => {
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const globalIdx = itemElMap.get(entry.target);
|
|
71
|
+
if (globalIdx === void 0) continue;
|
|
72
|
+
const h = entry.contentRect.height;
|
|
73
|
+
if (h > 0) patchHeight(globalIdx, h);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
const observeItem = (el, globalIdx) => {
|
|
77
|
+
itemElMap.set(el, globalIdx);
|
|
78
|
+
itemRo.observe(el);
|
|
79
|
+
};
|
|
80
|
+
const unobserveItem = (el) => {
|
|
81
|
+
itemRo.unobserve(el);
|
|
82
|
+
itemElMap.delete(el);
|
|
83
|
+
};
|
|
54
84
|
const visibleItems = computed(() => {
|
|
55
85
|
const top = scrollTop.value - props.overscan;
|
|
56
86
|
const bottom = scrollTop.value + viewportHeight.value + props.overscan;
|
|
@@ -73,23 +103,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
73
103
|
const containerOffset = containerRef.value ? containerRef.value.offsetTop : 0;
|
|
74
104
|
scrollTop.value = Math.max(0, st - containerOffset);
|
|
75
105
|
};
|
|
76
|
-
const itemElMap = /* @__PURE__ */ new WeakMap();
|
|
77
|
-
const itemRo = new ResizeObserver((entries) => {
|
|
78
|
-
for (const entry of entries) {
|
|
79
|
-
const globalIdx = itemElMap.get(entry.target);
|
|
80
|
-
if (globalIdx === void 0) continue;
|
|
81
|
-
const h = entry.contentRect.height;
|
|
82
|
-
if (h > 0) patchHeight(globalIdx, h);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
const observeItem = (el, globalIdx) => {
|
|
86
|
-
itemElMap.set(el, globalIdx);
|
|
87
|
-
itemRo.observe(el);
|
|
88
|
-
};
|
|
89
|
-
const unobserveItem = (el) => {
|
|
90
|
-
itemRo.unobserve(el);
|
|
91
|
-
itemElMap.delete(el);
|
|
92
|
-
};
|
|
93
106
|
let containerRo = null;
|
|
94
107
|
onMounted(async () => {
|
|
95
108
|
await nextTick();
|
|
@@ -109,13 +122,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
109
122
|
containerRo == null ? void 0 : containerRo.disconnect();
|
|
110
123
|
itemRo.disconnect();
|
|
111
124
|
});
|
|
112
|
-
watch(() => props.cols, () => {
|
|
113
|
-
reflow();
|
|
114
|
-
});
|
|
115
125
|
watch(() => props.items.length, (newLen, oldLen) => {
|
|
116
126
|
if (newLen < oldLen) measuredHeights.clear();
|
|
117
127
|
reflow();
|
|
118
128
|
});
|
|
129
|
+
watch(() => props.cols, () => {
|
|
130
|
+
reflow();
|
|
131
|
+
});
|
|
119
132
|
return (_ctx, _cache) => {
|
|
120
133
|
return openBlock(), createElementBlock("div", {
|
|
121
134
|
ref_key: "containerRef",
|
|
@@ -156,7 +169,7 @@ const _export_sfc = (sfc, props) => {
|
|
|
156
169
|
}
|
|
157
170
|
return target;
|
|
158
171
|
};
|
|
159
|
-
const Waterfall = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
172
|
+
const Waterfall = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4ad3e2ed"]]);
|
|
160
173
|
function install(app) {
|
|
161
174
|
app.component("TdWaterfall", Waterfall);
|
|
162
175
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/components/Waterfall.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'\n\nexport interface WaterfallProps {\n items: any[]\n /** Number of columns. Required when not using breakpoints externally. */\n cols: number\n gap?: number\n estimatedHeight?: number\n overscan?: number\n}\n\nconst props = withDefaults(defineProps<WaterfallProps>(), {\n gap: 15,\n estimatedHeight: 220,\n overscan: 600,\n})\n\nconst emit = defineEmits<{ (e: 'reflow'): void }>()\n\n// ─── types ────────────────────────────────────────────────────────────────────\n\ninterface PositionedItem {\n item: any\n globalIdx: number\n top: number\n height: number\n}\n\n// ─── state ────────────────────────────────────────────────────────────────────\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst scrollTop = ref(0)\nconst viewportHeight = ref(0)\n\n// colPositions[colIdx] = sorted array of positioned items\nconst colPositions = ref<PositionedItem[][]>([])\n// colTotalHeight[colIdx] = total height of that column (for the spacer div)\nconst colTotalHeights = ref<number[]>([])\n\n// globalIdx -> measured height (updated after image load)\nconst measuredHeights = new Map<number, number>()\n\n// ─── column count ─────────────────────────────────────────────────────────────\n\nconst getColCount = () => Math.max(1, props.cols)\n\n// ─── full reflow: re-assign items to columns ──────────────────────────────────\n// Only called when items list changes or viewport width changes.\n// Does NOT re-assign on height updates to avoid item jumping between columns.\n\nconst reflow = async () => {\n await nextTick()\n const count = getColCount()\n const cols: PositionedItem[][] = Array.from({ length: count }, () => [])\n const colH = new Array(count).fill(0)\n\n props.items.forEach((item, i) => {\n const minIdx = colH.indexOf(Math.min(...colH))\n const h = measuredHeights.get(i) ?? props.estimatedHeight\n cols[minIdx].push({ item, globalIdx: i, top: colH[minIdx], height: h })\n colH[minIdx] += h + props.gap\n })\n\n colPositions.value = cols\n colTotalHeights.value = colH\n emit('reflow')\n}\n\n// ─── patch: update one item's height in-place, shift subsequent items ─────────\n// Called after an image loads. Avoids full reflow to prevent column reassignment.\n\nconst patchHeight = (globalIdx: number, newHeight: number) => {\n const old = measuredHeights.get(globalIdx) ?? props.estimatedHeight\n if (Math.abs(old - newHeight) < 2) return\n measuredHeights.set(globalIdx, newHeight)\n\n for (let ci = 0; ci < colPositions.value.length; ci++) {\n const col = colPositions.value[ci]\n const pos = col.findIndex(p => p.globalIdx === globalIdx)\n if (pos === -1) continue\n\n const delta = newHeight - col[pos].height\n col[pos].height = newHeight\n\n for (let j = pos + 1; j < col.length; j++) {\n col[j] = { ...col[j], top: col[j].top + delta }\n }\n\n colTotalHeights.value[ci] += delta\n break\n }\n}\n\n// ─── virtual visibility ───────────────────────────────────────────────────────\n\nconst visibleItems = computed(() => {\n const top = scrollTop.value - props.overscan\n const bottom = scrollTop.value + viewportHeight.value + props.overscan\n return colPositions.value.map(col =>\n col.filter(p => p.top + p.height >= top && p.top <= bottom)\n )\n})\n\n// ─── scroll listener ──────────────────────────────────────────────────────────\n\nlet scrollEl: Element | null = null\n\nconst findScrollParent = (el: Element): Element => {\n let cur: Element | null = el.parentElement\n while (cur) {\n const { overflow, overflowY } = getComputedStyle(cur)\n if (/auto|scroll/.test(overflow + overflowY)) return cur\n cur = cur.parentElement\n }\n return document.documentElement\n}\n\nconst onScroll = (e: Event) => {\n const st = (e.target as Element).scrollTop\n const containerOffset = containerRef.value\n ? (containerRef.value as HTMLElement).offsetTop\n : 0\n scrollTop.value = Math.max(0, st - containerOffset)\n}\n\n// ─── item resize observer ─────────────────────────────────────────────────────\n// One shared RO watches every .wf-item element.\n// el -> globalIdx mapping is stored in a WeakMap so we never leak.\n\nconst itemElMap = new WeakMap<Element, number>()\n\nconst itemRo = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const globalIdx = itemElMap.get(entry.target)\n if (globalIdx === undefined) continue\n const h = entry.contentRect.height\n if (h > 0) patchHeight(globalIdx, h)\n }\n})\n\nconst observeItem = (el: Element, globalIdx: number) => {\n itemElMap.set(el, globalIdx)\n itemRo.observe(el)\n}\n\nconst unobserveItem = (el: Element) => {\n itemRo.unobserve(el)\n itemElMap.delete(el)\n}\n\n// ─── resize observer (container) ─────────────────────────────────────────────\n\nlet containerRo: ResizeObserver | null = null\n\n// ─── lifecycle ────────────────────────────────────────────────────────────────\n\nonMounted(async () => {\n await nextTick()\n if (!containerRef.value) return\n\n scrollEl = findScrollParent(containerRef.value)\n scrollEl.addEventListener('scroll', onScroll, { passive: true })\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n\n containerRo = new ResizeObserver(() => {\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n reflow()\n })\n containerRo.observe(containerRef.value)\n\n reflow()\n})\n\nonUnmounted(() => {\n scrollEl?.removeEventListener('scroll', onScroll)\n containerRo?.disconnect()\n itemRo.disconnect()\n})\n\nwatch(() => props.cols, () => {\n reflow()\n})\n\nwatch(() => props.items.length, (newLen, oldLen) => {\n if (newLen < oldLen) measuredHeights.clear()\n reflow()\n})\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"wf-root\" :style=\"{ gap: `${gap}px` }\">\n <div\n v-for=\"(_col, ci) in colPositions\"\n :key=\"ci\"\n class=\"wf-col\"\n :style=\"{ height: `${colTotalHeights[ci]}px` }\"\n >\n <div\n v-for=\"{ item, globalIdx, top } in visibleItems[ci]\"\n :key=\"globalIdx\"\n :ref=\"(el) => { if (el) observeItem(el as Element, globalIdx) }\"\n class=\"wf-item\"\n :style=\"{ transform: `translateY(${top}px)` }\"\n @vue:unmounted=\"({ el }: any) => unobserveItem(el)\"\n >\n <slot :item=\"item\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.wf-root {\n display: flex;\n align-items: flex-start;\n width: 100%;\n}\n\n.wf-col {\n flex: 1;\n position: relative;\n min-width: 0;\n}\n\n.wf-item {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n}\n</style>\n","import type { App, Plugin } from 'vue'\n\nimport Waterfall from './components/Waterfall.vue'\n\nexport { Waterfall }\nexport type { WaterfallProps } from './components/Waterfall.vue'\n\nfunction install(app: App): void {\n app.component('TdWaterfall', Waterfall)\n}\n\nconst TudouWaterfall: Plugin = {\n install,\n}\n\nexport default TudouWaterfall\nexport { TudouWaterfall }\n"],"names":["_createElementBlock","_openBlock","_Fragment","_renderList","_normalizeStyle","_renderSlot"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/components/Waterfall.vue","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'\n\nexport interface WaterfallBreakpoint {\n minWidth: number\n cols: number\n}\n\nexport interface WaterfallProps {\n items: any[]\n cols?: number\n gap?: number\n estimatedHeight?: number\n overscan?: number\n breakpoints?: WaterfallBreakpoint[]\n}\n\nconst props = withDefaults(defineProps<WaterfallProps>(), {\n cols: 0,\n gap: 15,\n estimatedHeight: 220,\n overscan: 600,\n breakpoints: () => [\n { minWidth: 1400, cols: 9 },\n { minWidth: 1200, cols: 8 },\n { minWidth: 900, cols: 7 },\n { minWidth: 700, cols: 6 },\n { minWidth: 500, cols: 5 },\n ],\n})\n\nconst emit = defineEmits<{ (e: 'reflow'): void }>()\n\n// ─── types ────────────────────────────────────────────────────────────────────\n\ninterface PositionedItem {\n item: any\n globalIdx: number\n top: number\n height: number\n}\n\n// ─── state ────────────────────────────────────────────────────────────────────\n\nconst containerRef = ref<HTMLElement | null>(null)\nconst scrollTop = ref(0)\nconst viewportHeight = ref(0)\n\nconst colPositions = ref<PositionedItem[][]>([])\nconst colTotalHeights = ref<number[]>([])\n\nconst measuredHeights = new Map<number, number>()\n\n// ─── column count ─────────────────────────────────────────────────────────────\n\nconst getColCount = () => {\n if (props.cols > 0) return props.cols\n const width = containerRef.value?.clientWidth ?? window.innerWidth\n const sorted = [...props.breakpoints].sort((a, b) => b.minWidth - a.minWidth)\n return sorted.find(bp => width >= bp.minWidth)?.cols ?? 2\n}\n\n// ─── full reflow ──────────────────────────────────────────────────────────────\n\nconst reflow = async () => {\n await nextTick()\n const count = getColCount()\n const cols: PositionedItem[][] = Array.from({ length: count }, () => [])\n const colH = new Array(count).fill(0)\n\n props.items.forEach((item, i) => {\n const minIdx = colH.indexOf(Math.min(...colH))\n const h = measuredHeights.get(i) ?? props.estimatedHeight\n cols[minIdx].push({ item, globalIdx: i, top: colH[minIdx], height: h })\n colH[minIdx] += h + props.gap\n })\n\n colPositions.value = cols\n colTotalHeights.value = colH\n emit('reflow')\n}\n\n// ─── patch height ─────────────────────────────────────────────────────────────\n\nconst patchHeight = (globalIdx: number, newHeight: number) => {\n const old = measuredHeights.get(globalIdx) ?? props.estimatedHeight\n if (Math.abs(old - newHeight) < 2) return\n measuredHeights.set(globalIdx, newHeight)\n\n for (let ci = 0; ci < colPositions.value.length; ci++) {\n const col = colPositions.value[ci]\n const pos = col.findIndex(p => p.globalIdx === globalIdx)\n if (pos === -1) continue\n\n const delta = newHeight - col[pos].height\n col[pos].height = newHeight\n\n for (let j = pos + 1; j < col.length; j++) {\n col[j] = { ...col[j], top: col[j].top + delta }\n }\n\n colTotalHeights.value[ci] += delta\n break\n }\n}\n\n// ─── auto height via ResizeObserver ──────────────────────────────────────────\n\nconst itemElMap = new WeakMap<Element, number>()\n\nconst itemRo = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const globalIdx = itemElMap.get(entry.target)\n if (globalIdx === undefined) continue\n const h = entry.contentRect.height\n if (h > 0) patchHeight(globalIdx, h)\n }\n})\n\nconst observeItem = (el: Element, globalIdx: number) => {\n itemElMap.set(el, globalIdx)\n itemRo.observe(el)\n}\n\nconst unobserveItem = (el: Element) => {\n itemRo.unobserve(el)\n itemElMap.delete(el)\n}\n\n// ─── virtual visibility ───────────────────────────────────────────────────────\n\nconst visibleItems = computed(() => {\n const top = scrollTop.value - props.overscan\n const bottom = scrollTop.value + viewportHeight.value + props.overscan\n return colPositions.value.map(col =>\n col.filter(p => p.top + p.height >= top && p.top <= bottom)\n )\n})\n\n// ─── scroll ───────────────────────────────────────────────────────────────────\n\nlet scrollEl: Element | null = null\n\nconst findScrollParent = (el: Element): Element => {\n let cur: Element | null = el.parentElement\n while (cur) {\n const { overflow, overflowY } = getComputedStyle(cur)\n if (/auto|scroll/.test(overflow + overflowY)) return cur\n cur = cur.parentElement\n }\n return document.documentElement\n}\n\nconst onScroll = (e: Event) => {\n const st = (e.target as Element).scrollTop\n const containerOffset = containerRef.value\n ? (containerRef.value as HTMLElement).offsetTop\n : 0\n scrollTop.value = Math.max(0, st - containerOffset)\n}\n\n// ─── lifecycle ────────────────────────────────────────────────────────────────\n\nlet containerRo: ResizeObserver | null = null\n\nonMounted(async () => {\n await nextTick()\n if (!containerRef.value) return\n\n scrollEl = findScrollParent(containerRef.value)\n scrollEl.addEventListener('scroll', onScroll, { passive: true })\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n\n containerRo = new ResizeObserver(() => {\n viewportHeight.value = scrollEl === document.documentElement\n ? window.innerHeight\n : (scrollEl as HTMLElement).clientHeight\n reflow()\n })\n containerRo.observe(containerRef.value)\n\n reflow()\n})\n\nonUnmounted(() => {\n scrollEl?.removeEventListener('scroll', onScroll)\n containerRo?.disconnect()\n itemRo.disconnect()\n})\n\nwatch(() => props.items.length, (newLen, oldLen) => {\n if (newLen < oldLen) measuredHeights.clear()\n reflow()\n})\n\nwatch(() => props.cols, () => {\n reflow()\n})\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"wf-root\" :style=\"{ gap: `${gap}px` }\">\n <div\n v-for=\"(_col, ci) in colPositions\"\n :key=\"ci\"\n class=\"wf-col\"\n :style=\"{ height: `${colTotalHeights[ci]}px` }\"\n >\n <div\n v-for=\"{ item, globalIdx, top } in visibleItems[ci]\"\n :key=\"globalIdx\"\n :ref=\"(el) => { if (el) observeItem(el as Element, globalIdx) }\"\n class=\"wf-item\"\n :style=\"{ transform: `translateY(${top}px)` }\"\n @vue:unmounted=\"({ el }: any) => unobserveItem(el)\"\n >\n <slot :item=\"item\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.wf-root {\n display: flex;\n align-items: flex-start;\n width: 100%;\n}\n\n.wf-col {\n flex: 1;\n position: relative;\n min-width: 0;\n}\n\n.wf-item {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n}\n</style>\n","import type { App, Plugin } from 'vue'\n\nimport Waterfall from './components/Waterfall.vue'\n\nexport { Waterfall }\nexport type { WaterfallProps, WaterfallBreakpoint } from './components/Waterfall.vue'\n\nfunction install(app: App): void {\n app.component('TdWaterfall', Waterfall)\n}\n\nconst TudouWaterfall: Plugin = {\n install,\n}\n\nexport default TudouWaterfall\nexport { TudouWaterfall }\n"],"names":["_createElementBlock","_openBlock","_Fragment","_renderList","_normalizeStyle","_renderSlot"],"mappings":"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"}
|
package/dist/style.css
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
|
|
2
|
-
.wf-root[data-v-
|
|
2
|
+
.wf-root[data-v-4ad3e2ed] {
|
|
3
3
|
display: flex;
|
|
4
4
|
align-items: flex-start;
|
|
5
5
|
width: 100%;
|
|
6
6
|
}
|
|
7
|
-
.wf-col[data-v-
|
|
7
|
+
.wf-col[data-v-4ad3e2ed] {
|
|
8
8
|
flex: 1;
|
|
9
9
|
position: relative;
|
|
10
10
|
min-width: 0;
|
|
11
11
|
}
|
|
12
|
-
.wf-item[data-v-
|
|
12
|
+
.wf-item[data-v-4ad3e2ed] {
|
|
13
13
|
position: absolute;
|
|
14
14
|
top: 0;
|
|
15
15
|
left: 0;
|