@witchcraft/ui 0.3.18 → 0.3.20
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/module.json +1 -1
- package/dist/runtime/assets/animations.css +1 -1
- package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue +8 -4
- package/dist/runtime/components/LibNotifications/LibNotification.vue +2 -2
- package/dist/runtime/components/LibNotifications/LibNotifications.d.vue.ts +1 -0
- package/dist/runtime/components/LibNotifications/LibNotifications.vue +34 -17
- package/dist/runtime/components/LibNotifications/LibNotifications.vue.d.ts +1 -0
- package/dist/runtime/components/LibNotifications/calculateNotificationProgress.d.ts +2 -0
- package/dist/runtime/components/LibNotifications/calculateNotificationProgress.js +4 -0
- package/dist/runtime/components/LibTable/LibTable.d.vue.ts +1 -0
- package/dist/runtime/components/LibTable/LibTable.vue +2 -2
- package/dist/runtime/components/LibTable/LibTable.vue.d.ts +1 -0
- package/dist/runtime/composables/useTimeConditionally.d.ts +16 -0
- package/dist/runtime/composables/useTimeConditionally.js +27 -0
- package/dist/runtime/directives/vResizableCols.js +20 -25
- package/dist/runtime/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/runtime/assets/animations.css +50 -3
- package/src/runtime/components/LibDatePicker/LibRangeDatePicker.vue +8 -4
- package/src/runtime/components/LibNotifications/LibNotification.vue +2 -2
- package/src/runtime/components/LibNotifications/LibNotifications.vue +35 -18
- package/src/runtime/components/LibNotifications/calculateNotificationProgress.ts +8 -0
- package/src/runtime/components/LibTable/LibTable.stories.ts +5 -4
- package/src/runtime/components/LibTable/LibTable.vue +3 -2
- package/src/runtime/composables/useTimeConditionally.ts +51 -0
- package/src/runtime/directives/vResizableCols.ts +20 -28
- package/src/runtime/types/index.ts +1 -1
package/dist/module.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@theme{--animate-blinkInf:blink 1s linear-infinite;@keyframes blink{0%{opacity:0}25%{opacity:1}75%{opacity:1}to{opacity:0}}--animate-slideBgInf:slide-bg 10s linear-infinite;@keyframes slide{0%{background-position:0}to{background-position:100%}}--animate-hide:hide 500ms ease-in;@keyframes hide{0%{opacity:1}to{opacity:0}}--animate-
|
|
1
|
+
@utility animate-from-*{--animate-from:--value([length,percentage]);--animate-from:calc(var(--spacing) * --value(integer))}@utility -animate-from-*{--animate-from:--value([length,percentage]);--animate-from:calc(var(--spacing) * -1 * --value(integer))}@theme{--animate-blinkInf:blink 1s linear-infinite;@keyframes blink{0%{opacity:0}25%{opacity:1}75%{opacity:1}to{opacity:0}}--animate-slideBgInf:slide-bg 10s linear-infinite;@keyframes slide{0%{background-position:0}to{background-position:100%}}--animate-hide:hide 500ms ease-in;@keyframes hide{0%{opacity:1}to{opacity:0}}--animate-slideInLeft:slideInLeft 500ms cubic-bezier(0.16,1,0.3,1);@keyframes slideInLeft{0%{opacity:0;transform:translateX(var(--animate-from,100%))}to{opacity:1;transform:translateX(0)}}--animate-slideInRight:slideInLeft 500ms cubic-bezier(0.16,1,0.3,1);@keyframes slideInRight{0%{opacity:0;transform:translateX(var(--animate-from,-100%))}to{opacity:1;transform:translateX(0)}}--animate-slideInUp:slideInUp 500ms cubic-bezier(0.16,1,0.3,1);@keyframes slideInUp{0%{opacity:0;transform:translateY(var(--animate-from,100%))}to{opacity:1;transform:translateY(0)}}--animate-slideInDown:slideInDown 500ms cubic-bezier(0.16,1,0.3,1);@keyframes slideInDown{0%{opacity:0;transform:translateY(var(--animate-from,-100%))}to{opacity:1;transform:translateY(0)}}--animate-overlayShow:overlayShow 500ms cubic-bezier(0.16,1,0.3,1);@keyframes overlayShow{0%{opacity:0}to{opacity:1}}--animate-contentShow:contentShow 500ms cubic-bezier(0.16,1,0.3,1);@keyframes contentShow{0%{opacity:0;transform:translateY(-10%) scale(.96)}to{opacity:1;transform:scale(1)}}}
|
|
@@ -209,10 +209,14 @@ const locale = useInjectedLocale().timeLocale;
|
|
|
209
209
|
dark:border-neutral-700
|
|
210
210
|
shadow-lg
|
|
211
211
|
will-change-[transform,opacity]
|
|
212
|
-
data-[
|
|
213
|
-
data-[
|
|
214
|
-
data-[
|
|
215
|
-
data-[
|
|
212
|
+
data-[side=top]:animate-from-3
|
|
213
|
+
data-[side=left]:animate-from-3
|
|
214
|
+
data-[side=right]:-animate-from-3
|
|
215
|
+
data-[side=bottom]:-animate-from-3
|
|
216
|
+
data-[state=open]:data-[side=top]:animate-slideInUp
|
|
217
|
+
data-[state=open]:data-[side=right]:animate-slideInRight
|
|
218
|
+
data-[state=open]:data-[side=bottom]:animate-slideInDown
|
|
219
|
+
data-[state=open]:data-[side=left]:animate-slideInLeft
|
|
216
220
|
text-fg
|
|
217
221
|
dark:text-neutral-200
|
|
218
222
|
"
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
:class="twMerge(
|
|
5
5
|
`
|
|
6
6
|
notification
|
|
7
|
-
max-w-700px
|
|
8
7
|
bg-neutral-50
|
|
9
8
|
dark:bg-neutral-900
|
|
10
9
|
text-fg
|
|
@@ -65,7 +64,7 @@
|
|
|
65
64
|
</div>
|
|
66
65
|
</slot>
|
|
67
66
|
<div class="notification--spacer flex-1"/>
|
|
68
|
-
<div class="actions flex">
|
|
67
|
+
<div class="notification--actions flex">
|
|
69
68
|
<LibButton
|
|
70
69
|
:border="false"
|
|
71
70
|
class="
|
|
@@ -203,6 +202,7 @@ onMounted(() => {
|
|
|
203
202
|
if (props.notification.isPaused) return;
|
|
204
203
|
emit("pause", props.notification);
|
|
205
204
|
} else {
|
|
205
|
+
if (!props.notification.isPaused) return;
|
|
206
206
|
emit("resume", props.notification);
|
|
207
207
|
}
|
|
208
208
|
}, { signal: mousedownAbortController.signal });
|
|
@@ -4,6 +4,7 @@ import type { HTMLAttributes } from "vue";
|
|
|
4
4
|
type RealProps = LinkableByIdProps & {
|
|
5
5
|
/** If not provided, uses the global handler (this requires useNotificationHandler be called and configured). */
|
|
6
6
|
handler?: NotificationHandler;
|
|
7
|
+
progressUpdateInterval?: number;
|
|
7
8
|
};
|
|
8
9
|
interface Props extends
|
|
9
10
|
/** @vue-ignore */
|
|
@@ -5,17 +5,16 @@
|
|
|
5
5
|
tag="div"
|
|
6
6
|
:class="twMerge(`
|
|
7
7
|
notifications
|
|
8
|
-
[--notification-width:
|
|
8
|
+
[--notification-width:calc(100dvw-var(--spacing)*4)]
|
|
9
|
+
sm:[--notification-width:300px]
|
|
9
10
|
fixed
|
|
10
11
|
top-0
|
|
11
12
|
z-50
|
|
12
13
|
right-[calc(var(--notification-width)*-1)]
|
|
13
14
|
w-[calc(var(--spacing)*2+var(--notification-width)*2)]
|
|
14
|
-
[&_.notification]:w-[var(--notification-width)]
|
|
15
15
|
max-h-[100dvh]
|
|
16
16
|
flex
|
|
17
17
|
flex-col
|
|
18
|
-
[&_.notification]:shrink-0
|
|
19
18
|
gap-1
|
|
20
19
|
list-none
|
|
21
20
|
outline-none
|
|
@@ -29,7 +28,16 @@
|
|
|
29
28
|
:handler="handler"
|
|
30
29
|
tabindex="0"
|
|
31
30
|
:notification="notification"
|
|
32
|
-
class="
|
|
31
|
+
class="
|
|
32
|
+
overflow-hidden
|
|
33
|
+
my-2
|
|
34
|
+
max-h-[300px]
|
|
35
|
+
w-[var(--notification-width)]
|
|
36
|
+
shrink-0
|
|
37
|
+
max-sm:[&_.notification--button]:p-2
|
|
38
|
+
max-sm:[&_.notification--button]:py-1
|
|
39
|
+
max-sm:[&_.notification--header]:text-lg
|
|
40
|
+
"
|
|
33
41
|
v-for="notification of notifications"
|
|
34
42
|
:key="notification.id"
|
|
35
43
|
@pause="handler.pause(notification)"
|
|
@@ -46,7 +54,7 @@
|
|
|
46
54
|
-mx-[calc(var(--spacing)*2+2px)]
|
|
47
55
|
rounded-none
|
|
48
56
|
"
|
|
49
|
-
:progress="
|
|
57
|
+
:progress="calculateNotificationProgress(notification, time)"
|
|
50
58
|
/>
|
|
51
59
|
</template>
|
|
52
60
|
</lib-notification>
|
|
@@ -67,19 +75,27 @@
|
|
|
67
75
|
<AlertDialogContent
|
|
68
76
|
class="
|
|
69
77
|
data-[state=open]:animate-contentShow
|
|
78
|
+
max-sm:data-[state=open]:animate-slideInUp
|
|
70
79
|
fixed
|
|
71
80
|
flex
|
|
81
|
+
max-h-[80dvh]
|
|
72
82
|
top-[50%]
|
|
73
83
|
left-[50%]
|
|
74
|
-
translate-x-[-50%]
|
|
75
|
-
translate-y-[-50%]
|
|
76
|
-
max-h-[80dvh]
|
|
84
|
+
sm:translate-x-[-50%]
|
|
85
|
+
sm:translate-y-[-50%]
|
|
77
86
|
max-w-[700px]
|
|
87
|
+
max-sm:bottom-2
|
|
88
|
+
max-sm:top-[unset]
|
|
89
|
+
max-sm:left-2
|
|
90
|
+
max-sm:right-2
|
|
91
|
+
max-sm:w-[calc(100%-var(--spacing)*4)]
|
|
78
92
|
z-100
|
|
79
93
|
"
|
|
80
94
|
>
|
|
81
95
|
<lib-notification
|
|
82
96
|
class="
|
|
97
|
+
w-full
|
|
98
|
+
sm:max-w-[700px]
|
|
83
99
|
max-w-full
|
|
84
100
|
max-h-full
|
|
85
101
|
top-notification
|
|
@@ -120,9 +136,11 @@ import {
|
|
|
120
136
|
AlertDialogRoot,
|
|
121
137
|
AlertDialogTitle
|
|
122
138
|
} from "reka-ui";
|
|
123
|
-
import { computed
|
|
139
|
+
import { computed } from "vue";
|
|
140
|
+
import { calculateNotificationProgress } from "./calculateNotificationProgress.js";
|
|
124
141
|
import LibNotification from "./LibNotification.vue";
|
|
125
142
|
import { useNotificationHandler } from "../../composables/useNotificationHandler.js";
|
|
143
|
+
import { useTimeConditionally } from "../../composables/useTimeConditionally.js";
|
|
126
144
|
import { NotificationHandler } from "../../helpers/NotificationHandler.js";
|
|
127
145
|
import { twMerge } from "../../utils/twMerge.js";
|
|
128
146
|
import LibProgressBar from "../LibProgressBar/LibProgressBar.vue";
|
|
@@ -132,17 +150,16 @@ defineOptions({
|
|
|
132
150
|
});
|
|
133
151
|
const props = defineProps({
|
|
134
152
|
id: { type: String, required: false },
|
|
135
|
-
handler: { type: Object, required: false }
|
|
153
|
+
handler: { type: Object, required: false },
|
|
154
|
+
progressUpdateInterval: { type: Number, required: false }
|
|
136
155
|
});
|
|
156
|
+
const handler = props.handler ?? useNotificationHandler();
|
|
137
157
|
const topNotifications = computed(() => handler.queue.filter((entry) => entry.requiresAction).reverse());
|
|
138
158
|
const notifications = computed(() => handler.queue.filter((entry) => !entry.requiresAction));
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
});
|
|
144
|
-
}, 50);
|
|
145
|
-
const handler = props.handler ?? useNotificationHandler();
|
|
159
|
+
const fetchTime = computed(() => {
|
|
160
|
+
return notifications.value.filter((entry) => entry.timeout !== void 0 && !entry.isPaused).length > 0;
|
|
161
|
+
});
|
|
162
|
+
const { time } = useTimeConditionally(fetchTime, { refreshInterval: props.progressUpdateInterval });
|
|
146
163
|
</script>
|
|
147
164
|
|
|
148
165
|
<script>
|
|
@@ -4,6 +4,7 @@ import type { HTMLAttributes } from "vue";
|
|
|
4
4
|
type RealProps = LinkableByIdProps & {
|
|
5
5
|
/** If not provided, uses the global handler (this requires useNotificationHandler be called and configured). */
|
|
6
6
|
handler?: NotificationHandler;
|
|
7
|
+
progressUpdateInterval?: number;
|
|
7
8
|
};
|
|
8
9
|
interface Props extends
|
|
9
10
|
/** @vue-ignore */
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export function calculateNotificationProgress(notification, time) {
|
|
2
|
+
if (notification.timeout === void 0) return 0;
|
|
3
|
+
return 100 - (notification.isPaused ? notification._timer.elapsedBeforePause : notification._timer.elapsedBeforePause + (time - notification.startTime)) / notification.timeout * 100;
|
|
4
|
+
}
|
|
@@ -11,6 +11,7 @@ type RealProps = {
|
|
|
11
11
|
rounded?: boolean;
|
|
12
12
|
border?: boolean;
|
|
13
13
|
cellBorder?: boolean;
|
|
14
|
+
/** Disables the header. This also sets the selector to `tr:first-child > td` instead to avoid issues with the vResizableCols directive. */
|
|
14
15
|
header?: boolean;
|
|
15
16
|
colConfig?: TableColConfig<T>;
|
|
16
17
|
/**
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
mergedVirtualizerOpts.enabled && mergedVirtualizerOpts.method === 'dynamic' && `
|
|
52
52
|
relative
|
|
53
53
|
`,
|
|
54
|
-
$attrs
|
|
54
|
+
$attrs['wrapper-class']
|
|
55
55
|
)"
|
|
56
56
|
ref="parentRef"
|
|
57
57
|
>
|
|
@@ -240,7 +240,7 @@ const isPostSetup = ref(false);
|
|
|
240
240
|
const resizableOptions = computed(() => ({
|
|
241
241
|
colCount: props.cols.length,
|
|
242
242
|
widths,
|
|
243
|
-
selector:
|
|
243
|
+
selector: props.header ? void 0 : `tr:first-child > td`,
|
|
244
244
|
...props.resizable,
|
|
245
245
|
onSetup: (el) => {
|
|
246
246
|
isPostSetup.value = true;
|
|
@@ -11,6 +11,7 @@ type RealProps = {
|
|
|
11
11
|
rounded?: boolean;
|
|
12
12
|
border?: boolean;
|
|
13
13
|
cellBorder?: boolean;
|
|
14
|
+
/** Disables the header. This also sets the selector to `tr:first-child > td` instead to avoid issues with the vResizableCols directive. */
|
|
14
15
|
header?: boolean;
|
|
15
16
|
colConfig?: TableColConfig<T>;
|
|
16
17
|
/**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
* Returns a ref with the current time if the given value ref is true or it's an array with 1 or more items.. It will update the time every 50ms (configurable) if so (within a requestAnimationFrame).
|
|
4
|
+
*
|
|
5
|
+
* When the value is anything else it will clear the interval and set the time to undefined.
|
|
6
|
+
*
|
|
7
|
+
* Useful for use with a progress bar.
|
|
8
|
+
*
|
|
9
|
+
* Updating the time all the time is expensive and not idea. This way we only set the interval if we really need it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useTimeConditionally(val: Ref<any[] | boolean | any>, { refreshInterval }?: {
|
|
12
|
+
refreshInterval?: number;
|
|
13
|
+
}): {
|
|
14
|
+
time: Ref<undefined | number>;
|
|
15
|
+
refresh: () => void;
|
|
16
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ref, watch } from "vue";
|
|
2
|
+
export function useTimeConditionally(val, {
|
|
3
|
+
refreshInterval = 50
|
|
4
|
+
} = {}) {
|
|
5
|
+
const time = ref(void 0);
|
|
6
|
+
let interval;
|
|
7
|
+
function refresh(v = val.value) {
|
|
8
|
+
if (v === true || Array.isArray(v) && v.length > 0) {
|
|
9
|
+
if (interval !== void 0) return;
|
|
10
|
+
time.value = Date.now();
|
|
11
|
+
interval = setInterval(() => {
|
|
12
|
+
requestAnimationFrame(() => {
|
|
13
|
+
time.value = Date.now();
|
|
14
|
+
});
|
|
15
|
+
}, refreshInterval);
|
|
16
|
+
} else {
|
|
17
|
+
if (interval === void 0) return;
|
|
18
|
+
clearInterval(interval);
|
|
19
|
+
interval = void 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
watch(val, (val2) => {
|
|
23
|
+
refresh(val2);
|
|
24
|
+
});
|
|
25
|
+
refresh();
|
|
26
|
+
return { time, refresh };
|
|
27
|
+
}
|
|
@@ -10,14 +10,8 @@ const defaultOpts = {
|
|
|
10
10
|
enabled: true
|
|
11
11
|
};
|
|
12
12
|
const callback = (_rect, el) => {
|
|
13
|
-
const $el = getElInfo(el);
|
|
14
|
-
if ($el.justResized) return;
|
|
15
13
|
setColWidths(el);
|
|
16
|
-
|
|
17
|
-
setTimeout(() => {
|
|
18
|
-
positionGrips(el);
|
|
19
|
-
$el.justResized = false;
|
|
20
|
-
}, 0);
|
|
14
|
+
positionGrips(el);
|
|
21
15
|
};
|
|
22
16
|
export const vResizableCols = {
|
|
23
17
|
mounted(el, { value: opts = {} }) {
|
|
@@ -29,7 +23,7 @@ export const vResizableCols = {
|
|
|
29
23
|
},
|
|
30
24
|
updated(el, { value: opts = {} }) {
|
|
31
25
|
const options = override({ ...defaultOpts }, opts);
|
|
32
|
-
const info = el && options.enabled && getElInfo(el);
|
|
26
|
+
const info = el && options.enabled && getElInfo(el, { throwIfMissing: false });
|
|
33
27
|
const hasGrips = el && options.enabled && elMap.get(el)?.grips;
|
|
34
28
|
const colsNotEqual = info && info.colCount !== options.colCount;
|
|
35
29
|
if (!options.enabled || colsNotEqual) {
|
|
@@ -54,7 +48,11 @@ function setWidth(col, amountInPx, el) {
|
|
|
54
48
|
const width = Math.max($el.margin, amountInPx);
|
|
55
49
|
const index = getColEls(el).findIndex((_) => col === _);
|
|
56
50
|
if ($el.fitWidth) {
|
|
57
|
-
|
|
51
|
+
if (amountInPx <= $el.margin) {
|
|
52
|
+
$el.widths.value[index] = `${$el.margin}px`;
|
|
53
|
+
} else {
|
|
54
|
+
$el.widths.value[index] = `${width / getBox(el).width * 100}%`;
|
|
55
|
+
}
|
|
58
56
|
} else {
|
|
59
57
|
$el.widths.value[index] = `${width}px`;
|
|
60
58
|
}
|
|
@@ -86,6 +84,7 @@ function createPointerDownHandler(el) {
|
|
|
86
84
|
castType(e.target);
|
|
87
85
|
$el.target = e.target;
|
|
88
86
|
$el.isDragging = true;
|
|
87
|
+
el.classList.add("dragging");
|
|
89
88
|
e.preventDefault();
|
|
90
89
|
document.addEventListener("pointerup", $el.pointerUpHandler);
|
|
91
90
|
const { col, colNext } = getCols(el);
|
|
@@ -133,11 +132,7 @@ function createPointerMoveHandler(el) {
|
|
|
133
132
|
setWidth(col, newWidth, el);
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
|
-
|
|
137
|
-
setTimeout(() => {
|
|
138
|
-
positionGrips(el);
|
|
139
|
-
$el.justResized = false;
|
|
140
|
-
}, 0);
|
|
135
|
+
positionGrips(el);
|
|
141
136
|
}
|
|
142
137
|
}
|
|
143
138
|
};
|
|
@@ -145,9 +140,11 @@ function createPointerMoveHandler(el) {
|
|
|
145
140
|
function createPointerUpHandler(el) {
|
|
146
141
|
return (e) => {
|
|
147
142
|
const $el = getElInfo(el);
|
|
143
|
+
$el.pointerMoveHandler(e);
|
|
148
144
|
if ($el.isDragging) {
|
|
149
145
|
e.preventDefault();
|
|
150
146
|
$el.isDragging = false;
|
|
147
|
+
el.classList.remove("dragging");
|
|
151
148
|
el.classList.remove("resizable-cols-error");
|
|
152
149
|
$el.offset = 0;
|
|
153
150
|
delete $el.target;
|
|
@@ -187,7 +184,7 @@ function getElInfo(el, { throwIfMissing = true } = {}) {
|
|
|
187
184
|
function getColEls(el) {
|
|
188
185
|
const $el = elMap.get(el);
|
|
189
186
|
if (!$el) unreachable("El went missing.");
|
|
190
|
-
return [...el.querySelectorAll(`:scope ${$el.selector
|
|
187
|
+
return [...el.querySelectorAll(`:scope ${$el.selector ?? "tr > th"}`)];
|
|
191
188
|
}
|
|
192
189
|
function setupColumns(el, opts) {
|
|
193
190
|
const gripWidth = getTestGripSize(el);
|
|
@@ -206,8 +203,10 @@ function setupColumns(el, opts) {
|
|
|
206
203
|
onTeardown: opts.onTeardown
|
|
207
204
|
};
|
|
208
205
|
elMap.set(el, $el);
|
|
209
|
-
const
|
|
210
|
-
|
|
206
|
+
const headers = getColEls(el);
|
|
207
|
+
if (headers.length !== opts.colCount) {
|
|
208
|
+
throw new Error(`Number of headers matched using selector ${opts.selector ?? "tr > th"} does not match number of columns.`);
|
|
209
|
+
}
|
|
211
210
|
setColWidths(el, headers);
|
|
212
211
|
el.style.width = $el.fitWidth ? "" : "min-content";
|
|
213
212
|
const len = opts.colCount;
|
|
@@ -218,13 +217,9 @@ function setupColumns(el, opts) {
|
|
|
218
217
|
el.appendChild(grip);
|
|
219
218
|
$el.grips.set(grip, i);
|
|
220
219
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
$el.justResized = false;
|
|
225
|
-
el.classList.add("resizable-cols-setup");
|
|
226
|
-
opts.onSetup?.(el);
|
|
227
|
-
}, 0);
|
|
220
|
+
positionGrips(el);
|
|
221
|
+
el.classList.add("resizable-cols-setup");
|
|
222
|
+
opts.onSetup?.(el);
|
|
228
223
|
}
|
|
229
224
|
function positionGrips(el) {
|
|
230
225
|
let xPos = 0;
|
|
@@ -291,7 +286,7 @@ function setColWidths(el, children) {
|
|
|
291
286
|
castType(col);
|
|
292
287
|
const colBox = getBox(col);
|
|
293
288
|
if ($el.fixedWidths[i] !== void 0) {
|
|
294
|
-
|
|
289
|
+
$el.widths.value[i] = `${$el.fixedWidths[i]}px`;
|
|
295
290
|
width += $el.fixedWidths[i];
|
|
296
291
|
} else {
|
|
297
292
|
if ($el.fitWidth) {
|
|
@@ -40,7 +40,7 @@ export type ResizableOptions = {
|
|
|
40
40
|
* It can then be used as needed by the component.
|
|
41
41
|
*/
|
|
42
42
|
widths: Ref<string[]>;
|
|
43
|
-
/** The selector to use for the cells. "tr >
|
|
43
|
+
/** The selector to use for the header cells. "tr > th" by default. */
|
|
44
44
|
selector: string;
|
|
45
45
|
/** Is called just after the `resizable-cols-setup` class is added. Can be useful for controlling the styling of wrappers or doing additional things post-setup. The default table element uses it to set the class on the wrapper also. */
|
|
46
46
|
onSetup?: (el: Element) => void;
|
package/package.json
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
@utility animate-from-* {
|
|
2
|
+
--animate-from: --value([length, percentage]);
|
|
3
|
+
--animate-from: calc(var(--spacing) * --value(integer));
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@utility -animate-from-* {
|
|
7
|
+
--animate-from: --value([length, percentage]);
|
|
8
|
+
--animate-from: calc(var(--spacing) * -1 * --value(integer));
|
|
9
|
+
}
|
|
10
|
+
|
|
1
11
|
@theme {
|
|
2
12
|
--animate-blinkInf: blink 1s linear-infinite;
|
|
3
13
|
@keyframes blink {
|
|
@@ -38,10 +48,10 @@
|
|
|
38
48
|
}
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
--animate-
|
|
42
|
-
@keyframes
|
|
51
|
+
--animate-slideInLeft: slideInLeft 500ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
52
|
+
@keyframes slideInLeft {
|
|
43
53
|
from {
|
|
44
|
-
transform: translateX(100%);
|
|
54
|
+
transform: translateX(var(--animate-from, 100%));
|
|
45
55
|
opacity: 0;
|
|
46
56
|
}
|
|
47
57
|
to {
|
|
@@ -50,6 +60,43 @@
|
|
|
50
60
|
}
|
|
51
61
|
}
|
|
52
62
|
|
|
63
|
+
--animate-slideInRight: slideInLeft 500ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
64
|
+
@keyframes slideInRight {
|
|
65
|
+
from {
|
|
66
|
+
transform: translateX(var(--animate-from, -100%));
|
|
67
|
+
opacity: 0;
|
|
68
|
+
}
|
|
69
|
+
to {
|
|
70
|
+
transform: translateX(0);
|
|
71
|
+
opacity: 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
--animate-slideInUp: slideInUp 500ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
76
|
+
@keyframes slideInUp {
|
|
77
|
+
from {
|
|
78
|
+
transform: translateY(var(--animate-from, 100%));
|
|
79
|
+
opacity: 0;
|
|
80
|
+
}
|
|
81
|
+
to {
|
|
82
|
+
transform: translateY(0);
|
|
83
|
+
opacity: 1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
--animate-slideInDown: slideInDown 500ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
88
|
+
@keyframes slideInDown {
|
|
89
|
+
from {
|
|
90
|
+
transform: translateY(var(--animate-from, -100%));
|
|
91
|
+
opacity: 0;
|
|
92
|
+
}
|
|
93
|
+
to {
|
|
94
|
+
transform: translateY(0);
|
|
95
|
+
opacity: 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
53
100
|
--animate-overlayShow: overlayShow 500ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
54
101
|
@keyframes overlayShow {
|
|
55
102
|
from {
|
|
@@ -237,10 +237,14 @@ const locale = useInjectedLocale().timeLocale
|
|
|
237
237
|
dark:border-neutral-700
|
|
238
238
|
shadow-lg
|
|
239
239
|
will-change-[transform,opacity]
|
|
240
|
-
data-[
|
|
241
|
-
data-[
|
|
242
|
-
data-[
|
|
243
|
-
data-[
|
|
240
|
+
data-[side=top]:animate-from-3
|
|
241
|
+
data-[side=left]:animate-from-3
|
|
242
|
+
data-[side=right]:-animate-from-3
|
|
243
|
+
data-[side=bottom]:-animate-from-3
|
|
244
|
+
data-[state=open]:data-[side=top]:animate-slideInUp
|
|
245
|
+
data-[state=open]:data-[side=right]:animate-slideInRight
|
|
246
|
+
data-[state=open]:data-[side=bottom]:animate-slideInDown
|
|
247
|
+
data-[state=open]:data-[side=left]:animate-slideInLeft
|
|
244
248
|
text-fg
|
|
245
249
|
dark:text-neutral-200
|
|
246
250
|
"
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
v-if="notification"
|
|
4
4
|
:class="twMerge(`
|
|
5
5
|
notification
|
|
6
|
-
max-w-700px
|
|
7
6
|
bg-neutral-50
|
|
8
7
|
dark:bg-neutral-900
|
|
9
8
|
text-fg
|
|
@@ -63,7 +62,7 @@
|
|
|
63
62
|
</div>
|
|
64
63
|
</slot>
|
|
65
64
|
<div class="notification--spacer flex-1"/>
|
|
66
|
-
<div class="actions flex">
|
|
65
|
+
<div class="notification--actions flex">
|
|
67
66
|
<LibButton
|
|
68
67
|
:border="false"
|
|
69
68
|
class="
|
|
@@ -221,6 +220,7 @@ onMounted(() => {
|
|
|
221
220
|
if (props.notification.isPaused) return
|
|
222
221
|
emit("pause", props.notification)
|
|
223
222
|
} else {
|
|
223
|
+
if (!props.notification.isPaused) return
|
|
224
224
|
emit("resume", props.notification)
|
|
225
225
|
}
|
|
226
226
|
}, { signal: mousedownAbortController.signal })
|
|
@@ -5,17 +5,16 @@
|
|
|
5
5
|
tag="div"
|
|
6
6
|
:class="twMerge(`
|
|
7
7
|
notifications
|
|
8
|
-
[--notification-width:
|
|
8
|
+
[--notification-width:calc(100dvw-var(--spacing)*4)]
|
|
9
|
+
sm:[--notification-width:300px]
|
|
9
10
|
fixed
|
|
10
11
|
top-0
|
|
11
12
|
z-50
|
|
12
13
|
right-[calc(var(--notification-width)*-1)]
|
|
13
14
|
w-[calc(var(--spacing)*2+var(--notification-width)*2)]
|
|
14
|
-
[&_.notification]:w-[var(--notification-width)]
|
|
15
15
|
max-h-[100dvh]
|
|
16
16
|
flex
|
|
17
17
|
flex-col
|
|
18
|
-
[&_.notification]:shrink-0
|
|
19
18
|
gap-1
|
|
20
19
|
list-none
|
|
21
20
|
outline-none
|
|
@@ -29,7 +28,16 @@
|
|
|
29
28
|
:handler="handler"
|
|
30
29
|
tabindex="0"
|
|
31
30
|
:notification="notification"
|
|
32
|
-
class="
|
|
31
|
+
class="
|
|
32
|
+
overflow-hidden
|
|
33
|
+
my-2
|
|
34
|
+
max-h-[300px]
|
|
35
|
+
w-[var(--notification-width)]
|
|
36
|
+
shrink-0
|
|
37
|
+
max-sm:[&_.notification--button]:p-2
|
|
38
|
+
max-sm:[&_.notification--button]:py-1
|
|
39
|
+
max-sm:[&_.notification--header]:text-lg
|
|
40
|
+
"
|
|
33
41
|
v-for="notification of notifications"
|
|
34
42
|
:key="notification.id"
|
|
35
43
|
@pause="handler.pause(notification)"
|
|
@@ -46,7 +54,7 @@
|
|
|
46
54
|
-mx-[calc(var(--spacing)*2+2px)]
|
|
47
55
|
rounded-none
|
|
48
56
|
"
|
|
49
|
-
:progress="
|
|
57
|
+
:progress="calculateNotificationProgress(notification, time!)"
|
|
50
58
|
/>
|
|
51
59
|
</template>
|
|
52
60
|
</lib-notification>
|
|
@@ -67,19 +75,27 @@
|
|
|
67
75
|
<AlertDialogContent
|
|
68
76
|
class="
|
|
69
77
|
data-[state=open]:animate-contentShow
|
|
78
|
+
max-sm:data-[state=open]:animate-slideInUp
|
|
70
79
|
fixed
|
|
71
80
|
flex
|
|
81
|
+
max-h-[80dvh]
|
|
72
82
|
top-[50%]
|
|
73
83
|
left-[50%]
|
|
74
|
-
translate-x-[-50%]
|
|
75
|
-
translate-y-[-50%]
|
|
76
|
-
max-h-[80dvh]
|
|
84
|
+
sm:translate-x-[-50%]
|
|
85
|
+
sm:translate-y-[-50%]
|
|
77
86
|
max-w-[700px]
|
|
87
|
+
max-sm:bottom-2
|
|
88
|
+
max-sm:top-[unset]
|
|
89
|
+
max-sm:left-2
|
|
90
|
+
max-sm:right-2
|
|
91
|
+
max-sm:w-[calc(100%-var(--spacing)*4)]
|
|
78
92
|
z-100
|
|
79
93
|
"
|
|
80
94
|
>
|
|
81
95
|
<lib-notification
|
|
82
96
|
class="
|
|
97
|
+
w-full
|
|
98
|
+
sm:max-w-[700px]
|
|
83
99
|
max-w-full
|
|
84
100
|
max-h-full
|
|
85
101
|
top-notification
|
|
@@ -120,11 +136,13 @@ import {
|
|
|
120
136
|
AlertDialogRoot,
|
|
121
137
|
AlertDialogTitle
|
|
122
138
|
} from "reka-ui"
|
|
123
|
-
import { computed
|
|
139
|
+
import { computed } from "vue"
|
|
124
140
|
|
|
141
|
+
import { calculateNotificationProgress } from "./calculateNotificationProgress.js"
|
|
125
142
|
import LibNotification from "./LibNotification.vue"
|
|
126
143
|
|
|
127
144
|
import { useNotificationHandler } from "../../composables/useNotificationHandler.js"
|
|
145
|
+
import { useTimeConditionally } from "../../composables/useTimeConditionally.js"
|
|
128
146
|
import { NotificationHandler } from "../../helpers/NotificationHandler.js"
|
|
129
147
|
import { twMerge } from "../../utils/twMerge.js"
|
|
130
148
|
import LibProgressBar from "../LibProgressBar/LibProgressBar.vue"
|
|
@@ -137,18 +155,16 @@ defineOptions({
|
|
|
137
155
|
|
|
138
156
|
const props = defineProps<Props>()
|
|
139
157
|
|
|
158
|
+
const handler = props.handler ?? useNotificationHandler()
|
|
159
|
+
|
|
140
160
|
const topNotifications = computed(() => handler.queue.filter(entry => entry.requiresAction).reverse())
|
|
141
161
|
const notifications = computed(() => handler.queue.filter(entry => !entry.requiresAction))
|
|
142
162
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
time.value = Date.now()
|
|
147
|
-
})
|
|
148
|
-
}, 50)
|
|
149
|
-
|
|
163
|
+
const fetchTime = computed(() => {
|
|
164
|
+
return notifications.value.filter(entry => entry.timeout !== undefined && !entry.isPaused).length > 0
|
|
165
|
+
})
|
|
150
166
|
|
|
151
|
-
const
|
|
167
|
+
const { time } = useTimeConditionally(fetchTime, { refreshInterval: props.progressUpdateInterval })
|
|
152
168
|
</script>
|
|
153
169
|
|
|
154
170
|
<script lang="ts">
|
|
@@ -157,8 +173,9 @@ import type { HTMLAttributes } from "vue"
|
|
|
157
173
|
type RealProps
|
|
158
174
|
= & LinkableByIdProps
|
|
159
175
|
& {
|
|
160
|
-
|
|
176
|
+
/** If not provided, uses the global handler (this requires useNotificationHandler be called and configured). */
|
|
161
177
|
handler?: NotificationHandler
|
|
178
|
+
progressUpdateInterval?: number
|
|
162
179
|
}
|
|
163
180
|
|
|
164
181
|
interface Props
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NotificationEntry } from "../../helpers/NotificationHandler.js"
|
|
2
|
+
|
|
3
|
+
export function calculateNotificationProgress(notification: NotificationEntry, time: number) {
|
|
4
|
+
if (notification.timeout === undefined) return 0
|
|
5
|
+
|
|
6
|
+
return 100 - (((notification.isPaused ? (notification._timer.elapsedBeforePause) : (notification._timer.elapsedBeforePause + (time - notification.startTime))) / notification.timeout) * 100)
|
|
7
|
+
}
|
|
8
|
+
|
|
@@ -19,8 +19,9 @@ const meta: Meta<typeof LibTable> = {
|
|
|
19
19
|
export default meta
|
|
20
20
|
type Story = StoryObj<typeof LibTable> & { args: {
|
|
21
21
|
slots?: string
|
|
22
|
-
|
|
22
|
+
["wrapper-class"]?: string
|
|
23
23
|
} }
|
|
24
|
+
|
|
24
25
|
export const Primary: Story = {
|
|
25
26
|
render: args => ({
|
|
26
27
|
components,
|
|
@@ -182,7 +183,7 @@ export const StickyHeader: Story = {
|
|
|
182
183
|
// moving the border to the wrapper is to hide the little bits of border sticking out
|
|
183
184
|
// added back the right straight border otherwise the scrollbar looks ass
|
|
184
185
|
// this is ever so slightly visible if there is no scrollbar
|
|
185
|
-
|
|
186
|
+
["wrapper-class"]: `
|
|
186
187
|
max-h-[50dvh]
|
|
187
188
|
`,
|
|
188
189
|
values: Array.from({ length: 200 }).fill(0).map((_, i) => ({
|
|
@@ -205,7 +206,7 @@ export const VirtualizedFixedHeight: Story = {
|
|
|
205
206
|
enabled: true
|
|
206
207
|
},
|
|
207
208
|
stickyHeader: true,
|
|
208
|
-
|
|
209
|
+
["wrapper-class"]: `
|
|
209
210
|
max-h-[50dvh]
|
|
210
211
|
`,
|
|
211
212
|
values: Array.from({ length: 10000 }).fill(0).map((_, i) => ({
|
|
@@ -258,7 +259,7 @@ export const VirtualizedFitWidthFalse: Story = {
|
|
|
258
259
|
[&:not(.resizable-cols-setup)]:w-max
|
|
259
260
|
[&:not(.resizable-cols-setup)_th]:w-max
|
|
260
261
|
`,
|
|
261
|
-
|
|
262
|
+
["wrapper-class"]: `
|
|
262
263
|
max-h-[50dvh]
|
|
263
264
|
`,
|
|
264
265
|
values: Array.from({ length: 10000 }).fill(0).map((_, i) => ({
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
mergedVirtualizerOpts.enabled && mergedVirtualizerOpts.method === 'dynamic' && `
|
|
51
51
|
relative
|
|
52
52
|
`,
|
|
53
|
-
($attrs as any)
|
|
53
|
+
($attrs as any)['wrapper-class'])"
|
|
54
54
|
ref="parentRef"
|
|
55
55
|
>
|
|
56
56
|
<div
|
|
@@ -256,7 +256,7 @@ const isPostSetup = ref(false)
|
|
|
256
256
|
const resizableOptions = computed<MakeRequired<Partial<ResizableOptions>, "colCount" | "widths">>(() => ({
|
|
257
257
|
colCount: props.cols.length,
|
|
258
258
|
widths,
|
|
259
|
-
selector:
|
|
259
|
+
selector: props.header ? undefined : `tr:first-child > td`,
|
|
260
260
|
...props.resizable,
|
|
261
261
|
onSetup: el => {
|
|
262
262
|
isPostSetup.value = true
|
|
@@ -414,6 +414,7 @@ type RealProps = {
|
|
|
414
414
|
rounded?: boolean
|
|
415
415
|
border?: boolean
|
|
416
416
|
cellBorder?: boolean
|
|
417
|
+
/** Disables the header. This also sets the selector to `tr:first-child > td` instead to avoid issues with the vResizableCols directive. */
|
|
417
418
|
header?: boolean
|
|
418
419
|
colConfig?: TableColConfig<T>
|
|
419
420
|
/**
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Ref, ref, watch } from "vue"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a ref with the current time if the given value ref is true or it's an array with 1 or more items.. It will update the time every 50ms (configurable) if so (within a requestAnimationFrame).
|
|
5
|
+
*
|
|
6
|
+
* When the value is anything else it will clear the interval and set the time to undefined.
|
|
7
|
+
*
|
|
8
|
+
* Useful for use with a progress bar.
|
|
9
|
+
*
|
|
10
|
+
* Updating the time all the time is expensive and not idea. This way we only set the interval if we really need it.
|
|
11
|
+
*/
|
|
12
|
+
export function useTimeConditionally(
|
|
13
|
+
val: Ref<any[] | boolean | any>,
|
|
14
|
+
{
|
|
15
|
+
refreshInterval = 50
|
|
16
|
+
}: {
|
|
17
|
+
refreshInterval?: number
|
|
18
|
+
} = {}
|
|
19
|
+
): {
|
|
20
|
+
time: Ref<undefined | number>
|
|
21
|
+
refresh: () => void
|
|
22
|
+
} {
|
|
23
|
+
const time = ref<undefined | number>(undefined)
|
|
24
|
+
|
|
25
|
+
let interval: ReturnType<typeof setInterval> | undefined
|
|
26
|
+
function refresh(
|
|
27
|
+
/** The value to use for the check. Do not pass unless you know what you're doing. */
|
|
28
|
+
v = val.value
|
|
29
|
+
) {
|
|
30
|
+
if (v === true || (Array.isArray(v) && v.length > 0)) {
|
|
31
|
+
if (interval !== undefined) return
|
|
32
|
+
time.value = Date.now()
|
|
33
|
+
interval = setInterval(() => {
|
|
34
|
+
requestAnimationFrame(() => {
|
|
35
|
+
time.value = Date.now()
|
|
36
|
+
})
|
|
37
|
+
}, refreshInterval)
|
|
38
|
+
} else {
|
|
39
|
+
if (interval === undefined) return
|
|
40
|
+
clearInterval(interval)
|
|
41
|
+
interval = undefined
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
watch(val, val => {
|
|
45
|
+
refresh(val)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
refresh()
|
|
49
|
+
|
|
50
|
+
return { time, refresh }
|
|
51
|
+
}
|
|
@@ -24,7 +24,6 @@ type Data = {
|
|
|
24
24
|
onTeardown?: (el: Element) => void
|
|
25
25
|
fixedWidths?: Record<number, number>
|
|
26
26
|
fluidWidthsAsPercentOfFluidWidth?: Record<number, number>
|
|
27
|
-
justResized?: boolean
|
|
28
27
|
}
|
|
29
28
|
const elMap = new WeakMap<HTMLElement, Data>()
|
|
30
29
|
type RawOpts = { value: Partial<ResizableOptions> }
|
|
@@ -40,14 +39,8 @@ const defaultOpts: Omit<ResizableOptions, "colCount" | "widths" | "selector"> =
|
|
|
40
39
|
// note that while it would be nice to throttle this it seems to loose the reference to the original element
|
|
41
40
|
// haven't found where the issue is yet #future
|
|
42
41
|
const callback: ResizeCallback = (_rect: DOMRectReadOnly, el: Element): void => {
|
|
43
|
-
const $el = getElInfo(el as ResizableElement)
|
|
44
|
-
if ($el.justResized) return
|
|
45
42
|
setColWidths(el as ResizableElement)
|
|
46
|
-
|
|
47
|
-
setTimeout(() => {
|
|
48
|
-
positionGrips(el as ResizableElement)
|
|
49
|
-
$el.justResized = false
|
|
50
|
-
}, 0)
|
|
43
|
+
positionGrips(el as ResizableElement)
|
|
51
44
|
}
|
|
52
45
|
|
|
53
46
|
/**
|
|
@@ -119,7 +112,7 @@ export const vResizableCols: Directive = {
|
|
|
119
112
|
},
|
|
120
113
|
updated(el: ResizableElement, { value: opts = {} }: RawOpts) {
|
|
121
114
|
const options = override({ ...defaultOpts }, opts) as ResizableOptions
|
|
122
|
-
const info = el && options.enabled && getElInfo(el)
|
|
115
|
+
const info = el && options.enabled && getElInfo(el, { throwIfMissing: false })
|
|
123
116
|
const hasGrips = el && options.enabled && elMap.get(el)?.grips
|
|
124
117
|
// todo, we should probably check by name
|
|
125
118
|
const colsNotEqual = (info && info.colCount !== options.colCount)
|
|
@@ -148,7 +141,11 @@ function setWidth(col: HTMLElement, amountInPx: number, el: ResizableElement): v
|
|
|
148
141
|
|
|
149
142
|
const index = getColEls(el).findIndex(_ => col === _)
|
|
150
143
|
if ($el.fitWidth) {
|
|
151
|
-
|
|
144
|
+
if (amountInPx <= $el.margin) {
|
|
145
|
+
$el.widths.value[index] = `${$el.margin}px`
|
|
146
|
+
} else {
|
|
147
|
+
$el.widths.value[index] = `${width / getBox(el).width * 100}%`
|
|
148
|
+
}
|
|
152
149
|
} else {
|
|
153
150
|
$el.widths.value[index] = `${width}px`
|
|
154
151
|
}
|
|
@@ -186,6 +183,7 @@ function createPointerDownHandler(el: ResizableElement) {
|
|
|
186
183
|
castType<HTMLElement>(e.target)
|
|
187
184
|
$el.target = e.target
|
|
188
185
|
$el.isDragging = true
|
|
186
|
+
el.classList.add("dragging")
|
|
189
187
|
e.preventDefault()
|
|
190
188
|
|
|
191
189
|
// in case any errors happen, we want the pointer up to still be called
|
|
@@ -245,11 +243,7 @@ function createPointerMoveHandler(el: ResizableElement) {
|
|
|
245
243
|
}
|
|
246
244
|
}
|
|
247
245
|
|
|
248
|
-
|
|
249
|
-
setTimeout(() => {
|
|
250
|
-
positionGrips(el)
|
|
251
|
-
$el.justResized = false
|
|
252
|
-
}, 0)
|
|
246
|
+
positionGrips(el)
|
|
253
247
|
}
|
|
254
248
|
}
|
|
255
249
|
}
|
|
@@ -258,9 +252,11 @@ function createPointerMoveHandler(el: ResizableElement) {
|
|
|
258
252
|
function createPointerUpHandler(el: ResizableElement) {
|
|
259
253
|
return (e: PointerEvent) => {
|
|
260
254
|
const $el = getElInfo(el)
|
|
255
|
+
$el.pointerMoveHandler(e)
|
|
261
256
|
if ($el.isDragging) {
|
|
262
257
|
e.preventDefault()
|
|
263
258
|
$el.isDragging = false
|
|
259
|
+
el.classList.remove("dragging")
|
|
264
260
|
el.classList.remove("resizable-cols-error")
|
|
265
261
|
$el.offset = 0
|
|
266
262
|
delete $el.target
|
|
@@ -303,7 +299,7 @@ function getElInfo<T extends boolean = true>(el: ResizableElement, { throwIfMiss
|
|
|
303
299
|
function getColEls(el: ResizableElement): HTMLElement[] {
|
|
304
300
|
const $el = elMap.get(el)
|
|
305
301
|
if (!$el) unreachable("El went missing.")
|
|
306
|
-
return [...el.querySelectorAll(`:scope ${$el.selector
|
|
302
|
+
return [...el.querySelectorAll(`:scope ${$el.selector ?? "tr > th"}`)] as any
|
|
307
303
|
}
|
|
308
304
|
|
|
309
305
|
function setupColumns(el: ResizableElement, opts: ResizableOptions): void {
|
|
@@ -323,9 +319,10 @@ function setupColumns(el: ResizableElement, opts: ResizableOptions): void {
|
|
|
323
319
|
onTeardown: opts.onTeardown
|
|
324
320
|
}
|
|
325
321
|
elMap.set(el, $el)
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
322
|
+
const headers = getColEls(el)
|
|
323
|
+
if (headers.length !== opts.colCount) {
|
|
324
|
+
throw new Error(`Number of headers matched using selector ${opts.selector ?? "tr > th"} does not match number of columns.`)
|
|
325
|
+
}
|
|
329
326
|
|
|
330
327
|
setColWidths(el, headers)
|
|
331
328
|
el.style.width = $el.fitWidth ? "" : "min-content"
|
|
@@ -338,13 +335,9 @@ function setupColumns(el: ResizableElement, opts: ResizableOptions): void {
|
|
|
338
335
|
el.appendChild(grip)
|
|
339
336
|
$el.grips.set(grip, i)
|
|
340
337
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
$el.justResized = false
|
|
345
|
-
el.classList.add("resizable-cols-setup")
|
|
346
|
-
opts.onSetup?.(el)
|
|
347
|
-
}, 0)
|
|
338
|
+
positionGrips(el)
|
|
339
|
+
el.classList.add("resizable-cols-setup")
|
|
340
|
+
opts.onSetup?.(el)
|
|
348
341
|
}
|
|
349
342
|
|
|
350
343
|
function positionGrips(el: ResizableElement): void {
|
|
@@ -412,7 +405,6 @@ function setColWidths(el: ResizableElement, children?: Element[]): void {
|
|
|
412
405
|
const minFlexWidth = (totalFluidCount * $el.margin)
|
|
413
406
|
const minTotalWidth = minFlexWidth + fixedTotalPx
|
|
414
407
|
|
|
415
|
-
|
|
416
408
|
let leftOverFluidWidth = elWidth - fixedTotalPx
|
|
417
409
|
if (leftOverFluidWidth < minFlexWidth) {
|
|
418
410
|
leftOverFluidWidth = minFlexWidth
|
|
@@ -428,7 +420,7 @@ function setColWidths(el: ResizableElement, children?: Element[]): void {
|
|
|
428
420
|
*/
|
|
429
421
|
const colBox = getBox(col)
|
|
430
422
|
if ($el.fixedWidths![i] !== undefined) {
|
|
431
|
-
|
|
423
|
+
$el.widths.value[i] = `${$el.fixedWidths![i]!}px`
|
|
432
424
|
width += $el.fixedWidths![i]!
|
|
433
425
|
} else {
|
|
434
426
|
if ($el.fitWidth) {
|
|
@@ -41,7 +41,7 @@ export type ResizableOptions = {
|
|
|
41
41
|
* It can then be used as needed by the component.
|
|
42
42
|
*/
|
|
43
43
|
widths: Ref<string[]>
|
|
44
|
-
/** The selector to use for the cells. "tr >
|
|
44
|
+
/** The selector to use for the header cells. "tr > th" by default. */
|
|
45
45
|
selector: string
|
|
46
46
|
/** Is called just after the `resizable-cols-setup` class is added. Can be useful for controlling the styling of wrappers or doing additional things post-setup. The default table element uses it to set the class on the wrapper also. */
|
|
47
47
|
onSetup?: (el: Element) => void
|