@caido-utils/frontend 0.1.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/BaseDialog.d.vue.ts +64 -0
- package/dist/components/BaseDialog.vue +89 -0
- package/dist/components/BaseDialog.vue.d.ts +64 -0
- package/dist/components/Card.d.vue.ts +12 -0
- package/dist/components/Card.vue +21 -0
- package/dist/components/Card.vue.d.ts +12 -0
- package/dist/components/Table.d.vue.ts +32 -0
- package/dist/components/Table.vue +320 -0
- package/dist/components/Table.vue.d.ts +32 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +3 -0
- package/dist/composables/index.d.ts +2 -0
- package/dist/composables/index.js +2 -0
- package/dist/composables/useDialogManager.d.ts +14 -0
- package/dist/composables/useDialogManager.js +23 -0
- package/dist/composables/useFrontend.d.ts +9 -0
- package/dist/composables/useFrontend.js +64 -0
- package/dist/utils/ctx.d.ts +8 -0
- package/dist/utils/ctx.js +21 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
title: string;
|
|
3
|
+
width: string;
|
|
4
|
+
showBack?: boolean;
|
|
5
|
+
showFooter?: boolean;
|
|
6
|
+
actionLabel?: string;
|
|
7
|
+
actionIcon?: string;
|
|
8
|
+
actionDisabled?: boolean;
|
|
9
|
+
actionLoading?: boolean;
|
|
10
|
+
cancelLabel?: string;
|
|
11
|
+
contentClass?: string;
|
|
12
|
+
};
|
|
13
|
+
declare var __VLS_26: {}, __VLS_28: {};
|
|
14
|
+
type __VLS_Slots = {} & {
|
|
15
|
+
default?: (props: typeof __VLS_26) => any;
|
|
16
|
+
} & {
|
|
17
|
+
footer?: (props: typeof __VLS_28) => any;
|
|
18
|
+
};
|
|
19
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToOption<__VLS_Props>, {
|
|
20
|
+
showBack: boolean;
|
|
21
|
+
showFooter: boolean;
|
|
22
|
+
actionLabel: string;
|
|
23
|
+
actionDisabled: boolean;
|
|
24
|
+
actionLoading: boolean;
|
|
25
|
+
cancelLabel: string;
|
|
26
|
+
}>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<__VLS_Props>, {
|
|
27
|
+
showBack: boolean;
|
|
28
|
+
showFooter: boolean;
|
|
29
|
+
actionLabel: string;
|
|
30
|
+
actionDisabled: boolean;
|
|
31
|
+
actionLoading: boolean;
|
|
32
|
+
cancelLabel: string;
|
|
33
|
+
}>>>, {
|
|
34
|
+
showBack: boolean;
|
|
35
|
+
showFooter: boolean;
|
|
36
|
+
actionLabel: string;
|
|
37
|
+
actionDisabled: boolean;
|
|
38
|
+
actionLoading: boolean;
|
|
39
|
+
cancelLabel: string;
|
|
40
|
+
}, {}>;
|
|
41
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
42
|
+
export default _default;
|
|
43
|
+
type __VLS_WithDefaults<P, D> = {
|
|
44
|
+
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_PrettifyLocal<P[K] & {
|
|
45
|
+
default: D[K];
|
|
46
|
+
}> : P[K];
|
|
47
|
+
};
|
|
48
|
+
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
49
|
+
type __VLS_TypePropsToOption<T> = {
|
|
50
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
51
|
+
type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
52
|
+
} : {
|
|
53
|
+
type: import('vue').PropType<T[K]>;
|
|
54
|
+
required: true;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
58
|
+
new (): {
|
|
59
|
+
$slots: S;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
type __VLS_PrettifyLocal<T> = {
|
|
63
|
+
[K in keyof T]: T[K];
|
|
64
|
+
} & {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Button from "primevue/button";
|
|
3
|
+
import Dialog from "primevue/dialog";
|
|
4
|
+
|
|
5
|
+
withDefaults(
|
|
6
|
+
defineProps<{
|
|
7
|
+
title: string;
|
|
8
|
+
width: string;
|
|
9
|
+
showBack?: boolean;
|
|
10
|
+
showFooter?: boolean;
|
|
11
|
+
actionLabel?: string;
|
|
12
|
+
actionIcon?: string;
|
|
13
|
+
actionDisabled?: boolean;
|
|
14
|
+
actionLoading?: boolean;
|
|
15
|
+
cancelLabel?: string;
|
|
16
|
+
contentClass?: string;
|
|
17
|
+
}>(),
|
|
18
|
+
{
|
|
19
|
+
showBack: false,
|
|
20
|
+
showFooter: true,
|
|
21
|
+
actionLabel: "Confirm",
|
|
22
|
+
actionDisabled: false,
|
|
23
|
+
actionLoading: false,
|
|
24
|
+
cancelLabel: "Cancel",
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const emit = defineEmits<{
|
|
29
|
+
close: [];
|
|
30
|
+
back: [];
|
|
31
|
+
action: [];
|
|
32
|
+
}>();
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<Dialog
|
|
37
|
+
:visible="true"
|
|
38
|
+
modal
|
|
39
|
+
:closable="false"
|
|
40
|
+
:style="{ width }"
|
|
41
|
+
:content-class="contentClass"
|
|
42
|
+
@update:visible="emit('close')"
|
|
43
|
+
>
|
|
44
|
+
<template #header>
|
|
45
|
+
<div class="flex items-center justify-between w-full">
|
|
46
|
+
<div class="flex items-center gap-3">
|
|
47
|
+
<Button
|
|
48
|
+
v-if="showBack"
|
|
49
|
+
icon="fas fa-arrow-left"
|
|
50
|
+
text
|
|
51
|
+
severity="secondary"
|
|
52
|
+
size="small"
|
|
53
|
+
@click="emit('back')"
|
|
54
|
+
/>
|
|
55
|
+
<span class="font-semibold text-lg">{{ title }}</span>
|
|
56
|
+
</div>
|
|
57
|
+
<Button
|
|
58
|
+
icon="fas fa-times"
|
|
59
|
+
text
|
|
60
|
+
severity="secondary"
|
|
61
|
+
size="small"
|
|
62
|
+
@click="emit('close')"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<slot />
|
|
68
|
+
|
|
69
|
+
<template v-if="showFooter" #footer>
|
|
70
|
+
<slot name="footer">
|
|
71
|
+
<div class="flex justify-end gap-2">
|
|
72
|
+
<Button
|
|
73
|
+
:label="cancelLabel"
|
|
74
|
+
severity="secondary"
|
|
75
|
+
text
|
|
76
|
+
@click="emit('close')"
|
|
77
|
+
/>
|
|
78
|
+
<Button
|
|
79
|
+
:label="actionLabel"
|
|
80
|
+
:icon="actionIcon"
|
|
81
|
+
:disabled="actionDisabled"
|
|
82
|
+
:loading="actionLoading"
|
|
83
|
+
@click="emit('action')"
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</slot>
|
|
87
|
+
</template>
|
|
88
|
+
</Dialog>
|
|
89
|
+
</template>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
title: string;
|
|
3
|
+
width: string;
|
|
4
|
+
showBack?: boolean;
|
|
5
|
+
showFooter?: boolean;
|
|
6
|
+
actionLabel?: string;
|
|
7
|
+
actionIcon?: string;
|
|
8
|
+
actionDisabled?: boolean;
|
|
9
|
+
actionLoading?: boolean;
|
|
10
|
+
cancelLabel?: string;
|
|
11
|
+
contentClass?: string;
|
|
12
|
+
};
|
|
13
|
+
declare var __VLS_26: {}, __VLS_28: {};
|
|
14
|
+
type __VLS_Slots = {} & {
|
|
15
|
+
default?: (props: typeof __VLS_26) => any;
|
|
16
|
+
} & {
|
|
17
|
+
footer?: (props: typeof __VLS_28) => any;
|
|
18
|
+
};
|
|
19
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToOption<__VLS_Props>, {
|
|
20
|
+
showBack: boolean;
|
|
21
|
+
showFooter: boolean;
|
|
22
|
+
actionLabel: string;
|
|
23
|
+
actionDisabled: boolean;
|
|
24
|
+
actionLoading: boolean;
|
|
25
|
+
cancelLabel: string;
|
|
26
|
+
}>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<__VLS_Props>, {
|
|
27
|
+
showBack: boolean;
|
|
28
|
+
showFooter: boolean;
|
|
29
|
+
actionLabel: string;
|
|
30
|
+
actionDisabled: boolean;
|
|
31
|
+
actionLoading: boolean;
|
|
32
|
+
cancelLabel: string;
|
|
33
|
+
}>>>, {
|
|
34
|
+
showBack: boolean;
|
|
35
|
+
showFooter: boolean;
|
|
36
|
+
actionLabel: string;
|
|
37
|
+
actionDisabled: boolean;
|
|
38
|
+
actionLoading: boolean;
|
|
39
|
+
cancelLabel: string;
|
|
40
|
+
}, {}>;
|
|
41
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
42
|
+
export default _default;
|
|
43
|
+
type __VLS_WithDefaults<P, D> = {
|
|
44
|
+
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_PrettifyLocal<P[K] & {
|
|
45
|
+
default: D[K];
|
|
46
|
+
}> : P[K];
|
|
47
|
+
};
|
|
48
|
+
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
49
|
+
type __VLS_TypePropsToOption<T> = {
|
|
50
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
51
|
+
type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
52
|
+
} : {
|
|
53
|
+
type: import('vue').PropType<T[K]>;
|
|
54
|
+
required: true;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
58
|
+
new (): {
|
|
59
|
+
$slots: S;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
type __VLS_PrettifyLocal<T> = {
|
|
63
|
+
[K in keyof T]: T[K];
|
|
64
|
+
} & {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare var __VLS_7: any, __VLS_8: any;
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
|
6
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
+
export default _default;
|
|
8
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
+
new (): {
|
|
10
|
+
$slots: S;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import PrimeCard from "primevue/card";
|
|
3
|
+
|
|
4
|
+
defineOptions({
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
});
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<PrimeCard
|
|
11
|
+
v-bind="$attrs"
|
|
12
|
+
:pt="{
|
|
13
|
+
body: { class: 'h-full p-0' },
|
|
14
|
+
content: { class: 'h-full flex flex-col' },
|
|
15
|
+
}"
|
|
16
|
+
>
|
|
17
|
+
<template v-for="(_, slot) in $slots" #[slot]="scope">
|
|
18
|
+
<slot :name="slot" v-bind="scope || {}" />
|
|
19
|
+
</template>
|
|
20
|
+
</PrimeCard>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare var __VLS_7: any, __VLS_8: any;
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
|
6
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
+
export default _default;
|
|
8
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
+
new (): {
|
|
10
|
+
$slots: S;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
declare const _default: <T extends Record<string, unknown>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
2
|
+
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & Readonly<import("vue").ExtractPropTypes<{}>>, never>, never> & {
|
|
3
|
+
items: T[];
|
|
4
|
+
selected: T[];
|
|
5
|
+
columns: {
|
|
6
|
+
key: string;
|
|
7
|
+
header: string;
|
|
8
|
+
width: string;
|
|
9
|
+
sortable?: boolean;
|
|
10
|
+
sortType?: "string" | "number" | "date";
|
|
11
|
+
}[];
|
|
12
|
+
rowKey: (item: T) => string;
|
|
13
|
+
activeKey?: string;
|
|
14
|
+
emptyMessage?: string;
|
|
15
|
+
selectable?: boolean;
|
|
16
|
+
} & Partial<{}>> & import("vue").PublicProps;
|
|
17
|
+
expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
|
|
18
|
+
attrs: any;
|
|
19
|
+
slots: {
|
|
20
|
+
[x: string]: ((props: {
|
|
21
|
+
item: any;
|
|
22
|
+
value: any;
|
|
23
|
+
}) => any) | undefined;
|
|
24
|
+
};
|
|
25
|
+
emit: ((evt: "update:selected", value: T[]) => void) & ((evt: "row-click", item: T) => void);
|
|
26
|
+
}>) => import("vue").VNode & {
|
|
27
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
28
|
+
};
|
|
29
|
+
export default _default;
|
|
30
|
+
type __VLS_PrettifyLocal<T> = {
|
|
31
|
+
[K in keyof T]: T[K];
|
|
32
|
+
} & {};
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends Record<string, unknown>">
|
|
2
|
+
import VirtualScroller from "primevue/virtualscroller";
|
|
3
|
+
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
4
|
+
|
|
5
|
+
type SortType = "string" | "number" | "date";
|
|
6
|
+
|
|
7
|
+
type Column = {
|
|
8
|
+
key: string;
|
|
9
|
+
header: string;
|
|
10
|
+
width: string;
|
|
11
|
+
sortable?: boolean;
|
|
12
|
+
sortType?: SortType;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type SortOrder = "asc" | "desc" | undefined;
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(
|
|
18
|
+
defineProps<{
|
|
19
|
+
items: T[];
|
|
20
|
+
selected: T[];
|
|
21
|
+
columns: Column[];
|
|
22
|
+
rowKey: (item: T) => string;
|
|
23
|
+
activeKey?: string;
|
|
24
|
+
emptyMessage?: string;
|
|
25
|
+
selectable?: boolean;
|
|
26
|
+
}>(),
|
|
27
|
+
{ selectable: false },
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const emit = defineEmits<{
|
|
31
|
+
"update:selected": [value: T[]];
|
|
32
|
+
"row-click": [item: T];
|
|
33
|
+
}>();
|
|
34
|
+
|
|
35
|
+
const selectedKeys = ref(new Set<string>());
|
|
36
|
+
|
|
37
|
+
watch(
|
|
38
|
+
() => props.selected,
|
|
39
|
+
(val) => {
|
|
40
|
+
const keys = new Set<string>();
|
|
41
|
+
for (const item of val) {
|
|
42
|
+
keys.add(props.rowKey(item));
|
|
43
|
+
}
|
|
44
|
+
selectedKeys.value = keys;
|
|
45
|
+
},
|
|
46
|
+
{ immediate: true },
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
function syncSelection() {
|
|
50
|
+
const result: T[] = [];
|
|
51
|
+
for (const item of props.items) {
|
|
52
|
+
if (selectedKeys.value.has(props.rowKey(item))) {
|
|
53
|
+
result.push(item);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
emit("update:selected", result);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function toggleSelection(item: T) {
|
|
60
|
+
const key = props.rowKey(item);
|
|
61
|
+
if (selectedKeys.value.has(key)) {
|
|
62
|
+
selectedKeys.value.delete(key);
|
|
63
|
+
} else {
|
|
64
|
+
selectedKeys.value.add(key);
|
|
65
|
+
}
|
|
66
|
+
syncSelection();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function toggleAll() {
|
|
70
|
+
if (selectedKeys.value.size === props.items.length) {
|
|
71
|
+
selectedKeys.value.clear();
|
|
72
|
+
} else {
|
|
73
|
+
for (const item of props.items) {
|
|
74
|
+
selectedKeys.value.add(props.rowKey(item));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
syncSelection();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const allSelected = computed(
|
|
81
|
+
() =>
|
|
82
|
+
props.items.length > 0 && selectedKeys.value.size === props.items.length,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
function onRowClick(item: T) {
|
|
86
|
+
emit("row-click", item);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isSelected(item: T): boolean {
|
|
90
|
+
return selectedKeys.value.has(props.rowKey(item));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isActive(item: T): boolean {
|
|
94
|
+
if (props.activeKey === undefined) return false;
|
|
95
|
+
return props.rowKey(item) === props.activeKey;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const sortKey = ref<string | undefined>(undefined);
|
|
99
|
+
const sortOrder = ref<SortOrder>(undefined);
|
|
100
|
+
|
|
101
|
+
function toggleSort(col: Column) {
|
|
102
|
+
if (!col.sortable) return;
|
|
103
|
+
|
|
104
|
+
if (sortKey.value !== col.key) {
|
|
105
|
+
sortKey.value = col.key;
|
|
106
|
+
sortOrder.value = "asc";
|
|
107
|
+
} else if (sortOrder.value === "asc") {
|
|
108
|
+
sortOrder.value = "desc";
|
|
109
|
+
} else {
|
|
110
|
+
sortKey.value = undefined;
|
|
111
|
+
sortOrder.value = undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const sortedItems = computed(() => {
|
|
116
|
+
if (sortKey.value === undefined || sortOrder.value === undefined) {
|
|
117
|
+
return props.items;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const key = sortKey.value;
|
|
121
|
+
const col = props.columns.find((c) => c.key === key);
|
|
122
|
+
const type = col?.sortType ?? "string";
|
|
123
|
+
const dir = sortOrder.value === "asc" ? 1 : -1;
|
|
124
|
+
const sorted = [...props.items];
|
|
125
|
+
|
|
126
|
+
sorted.sort((a, b) => {
|
|
127
|
+
const aVal = a[key];
|
|
128
|
+
const bVal = b[key];
|
|
129
|
+
|
|
130
|
+
if (aVal === undefined || aVal === "") return 1;
|
|
131
|
+
if (bVal === undefined || bVal === "") return -1;
|
|
132
|
+
|
|
133
|
+
if (type === "number") {
|
|
134
|
+
return (Number(aVal) - Number(bVal)) * dir;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (type === "date") {
|
|
138
|
+
return (
|
|
139
|
+
(new Date(String(aVal)).getTime() - new Date(String(bVal)).getTime()) *
|
|
140
|
+
dir
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return String(aVal).localeCompare(String(bVal)) * dir;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return sorted;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const VIRTUAL_THRESHOLD = 100;
|
|
151
|
+
const useVirtual = computed(
|
|
152
|
+
() => sortedItems.value.length >= VIRTUAL_THRESHOLD,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const scrollerKey = ref(0);
|
|
156
|
+
const containerRef = ref<HTMLElement>();
|
|
157
|
+
let resizeObserver: ResizeObserver | undefined;
|
|
158
|
+
|
|
159
|
+
onMounted(() => {
|
|
160
|
+
if (containerRef.value === undefined) return;
|
|
161
|
+
resizeObserver = new ResizeObserver((entries) => {
|
|
162
|
+
const entry = entries[0];
|
|
163
|
+
if (entry !== undefined && entry.contentRect.height > 0) {
|
|
164
|
+
scrollerKey.value++;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
resizeObserver.observe(containerRef.value);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
onBeforeUnmount(() => {
|
|
171
|
+
resizeObserver?.disconnect();
|
|
172
|
+
});
|
|
173
|
+
</script>
|
|
174
|
+
|
|
175
|
+
<template>
|
|
176
|
+
<div class="h-full flex flex-col min-h-0">
|
|
177
|
+
<table class="w-full border-collapse" :style="{ tableLayout: 'fixed' }">
|
|
178
|
+
<thead>
|
|
179
|
+
<tr>
|
|
180
|
+
<th
|
|
181
|
+
v-if="selectable"
|
|
182
|
+
class="w-8 px-2 py-1.5 bg-surface-900 sticky top-0 z-10"
|
|
183
|
+
>
|
|
184
|
+
<div
|
|
185
|
+
class="w-3.5 h-3.5 rounded-sm border cursor-pointer flex items-center justify-center transition-colors"
|
|
186
|
+
:class="
|
|
187
|
+
allSelected
|
|
188
|
+
? 'bg-amber-500 border-amber-500'
|
|
189
|
+
: 'border-surface-500 hover:border-surface-300'
|
|
190
|
+
"
|
|
191
|
+
@click="toggleAll()"
|
|
192
|
+
>
|
|
193
|
+
<i
|
|
194
|
+
v-if="allSelected"
|
|
195
|
+
class="fas fa-check text-[8px] text-white"
|
|
196
|
+
/>
|
|
197
|
+
</div>
|
|
198
|
+
</th>
|
|
199
|
+
<th
|
|
200
|
+
v-for="col in columns"
|
|
201
|
+
:key="col.key"
|
|
202
|
+
class="px-2.5 py-1.5 font-mono text-sm font-semibold text-surface-300 text-left bg-surface-900 border-b border-surface-700/50 sticky top-0 z-10 select-none"
|
|
203
|
+
:class="
|
|
204
|
+
col.sortable
|
|
205
|
+
? 'cursor-pointer hover:text-surface-100 transition-colors'
|
|
206
|
+
: ''
|
|
207
|
+
"
|
|
208
|
+
:style="{ width: col.width }"
|
|
209
|
+
@click="toggleSort(col)"
|
|
210
|
+
>
|
|
211
|
+
<span class="flex items-center gap-1.5">
|
|
212
|
+
{{ col.header }}
|
|
213
|
+
<i
|
|
214
|
+
v-if="col.sortable && sortKey === col.key"
|
|
215
|
+
:class="[
|
|
216
|
+
'fas text-[9px]',
|
|
217
|
+
sortOrder === 'asc' ? 'fa-chevron-up' : 'fa-chevron-down',
|
|
218
|
+
]"
|
|
219
|
+
/>
|
|
220
|
+
</span>
|
|
221
|
+
</th>
|
|
222
|
+
</tr>
|
|
223
|
+
</thead>
|
|
224
|
+
</table>
|
|
225
|
+
<div
|
|
226
|
+
ref="containerRef"
|
|
227
|
+
class="flex-1 min-h-0"
|
|
228
|
+
:class="useVirtual ? 'overflow-hidden' : 'overflow-y-auto'"
|
|
229
|
+
>
|
|
230
|
+
<div
|
|
231
|
+
v-if="sortedItems.length === 0"
|
|
232
|
+
class="flex flex-col items-center justify-center h-full gap-3 text-surface-500"
|
|
233
|
+
>
|
|
234
|
+
<i class="fas fa-inbox text-3xl text-surface-600" />
|
|
235
|
+
<span class="text-sm">{{ emptyMessage ?? "No data" }}</span>
|
|
236
|
+
</div>
|
|
237
|
+
<VirtualScroller
|
|
238
|
+
v-else
|
|
239
|
+
:key="scrollerKey"
|
|
240
|
+
:items="sortedItems"
|
|
241
|
+
:item-size="33"
|
|
242
|
+
:disabled="!useVirtual"
|
|
243
|
+
class="h-full"
|
|
244
|
+
>
|
|
245
|
+
<template
|
|
246
|
+
#content="{
|
|
247
|
+
items: visibleItems,
|
|
248
|
+
styleClass,
|
|
249
|
+
contentRef,
|
|
250
|
+
contentStyle,
|
|
251
|
+
}"
|
|
252
|
+
>
|
|
253
|
+
<table
|
|
254
|
+
:ref="contentRef"
|
|
255
|
+
:class="styleClass"
|
|
256
|
+
class="w-full border-collapse"
|
|
257
|
+
:style="{ tableLayout: 'fixed', ...contentStyle }"
|
|
258
|
+
>
|
|
259
|
+
<colgroup>
|
|
260
|
+
<col v-if="selectable" style="width: 32px" />
|
|
261
|
+
<col
|
|
262
|
+
v-for="col in columns"
|
|
263
|
+
:key="col.key"
|
|
264
|
+
:style="{ width: col.width }"
|
|
265
|
+
/>
|
|
266
|
+
</colgroup>
|
|
267
|
+
<tbody>
|
|
268
|
+
<tr
|
|
269
|
+
v-for="item in visibleItems as T[]"
|
|
270
|
+
:key="rowKey(item)"
|
|
271
|
+
class="cursor-pointer transition-colors"
|
|
272
|
+
:class="[
|
|
273
|
+
isActive(item)
|
|
274
|
+
? 'bg-amber-400/15 hover:bg-amber-400/25'
|
|
275
|
+
: isSelected(item)
|
|
276
|
+
? 'bg-amber-400/10 hover:bg-amber-400/15'
|
|
277
|
+
: 'even:bg-white/[0.02] hover:bg-white/[0.04]',
|
|
278
|
+
]"
|
|
279
|
+
@click="onRowClick(item)"
|
|
280
|
+
>
|
|
281
|
+
<td
|
|
282
|
+
v-if="selectable"
|
|
283
|
+
class="px-2 py-1.5 border-b border-surface-700/50"
|
|
284
|
+
@click.stop="toggleSelection(item)"
|
|
285
|
+
>
|
|
286
|
+
<div
|
|
287
|
+
class="w-3.5 h-3.5 rounded-sm border cursor-pointer flex items-center justify-center transition-colors"
|
|
288
|
+
:class="
|
|
289
|
+
isSelected(item)
|
|
290
|
+
? 'bg-amber-500 border-amber-500'
|
|
291
|
+
: 'border-surface-600 hover:border-surface-400'
|
|
292
|
+
"
|
|
293
|
+
>
|
|
294
|
+
<i
|
|
295
|
+
v-if="isSelected(item)"
|
|
296
|
+
class="fas fa-check text-[8px] text-white"
|
|
297
|
+
/>
|
|
298
|
+
</div>
|
|
299
|
+
</td>
|
|
300
|
+
<td
|
|
301
|
+
v-for="col in columns"
|
|
302
|
+
:key="col.key"
|
|
303
|
+
class="px-2.5 py-1.5 font-mono text-[13px] font-medium text-surface-200 overflow-hidden text-ellipsis whitespace-nowrap border-b border-surface-700/50"
|
|
304
|
+
>
|
|
305
|
+
<slot
|
|
306
|
+
:name="`cell-${col.key}`"
|
|
307
|
+
:item="item"
|
|
308
|
+
:value="item[col.key]"
|
|
309
|
+
>
|
|
310
|
+
<span class="truncate block">{{ item[col.key] }}</span>
|
|
311
|
+
</slot>
|
|
312
|
+
</td>
|
|
313
|
+
</tr>
|
|
314
|
+
</tbody>
|
|
315
|
+
</table>
|
|
316
|
+
</template>
|
|
317
|
+
</VirtualScroller>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
</template>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
declare const _default: <T extends Record<string, unknown>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
2
|
+
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & Readonly<import("vue").ExtractPropTypes<{}>>, never>, never> & {
|
|
3
|
+
items: T[];
|
|
4
|
+
selected: T[];
|
|
5
|
+
columns: {
|
|
6
|
+
key: string;
|
|
7
|
+
header: string;
|
|
8
|
+
width: string;
|
|
9
|
+
sortable?: boolean;
|
|
10
|
+
sortType?: "string" | "number" | "date";
|
|
11
|
+
}[];
|
|
12
|
+
rowKey: (item: T) => string;
|
|
13
|
+
activeKey?: string;
|
|
14
|
+
emptyMessage?: string;
|
|
15
|
+
selectable?: boolean;
|
|
16
|
+
} & Partial<{}>> & import("vue").PublicProps;
|
|
17
|
+
expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
|
|
18
|
+
attrs: any;
|
|
19
|
+
slots: {
|
|
20
|
+
[x: string]: ((props: {
|
|
21
|
+
item: any;
|
|
22
|
+
value: any;
|
|
23
|
+
}) => any) | undefined;
|
|
24
|
+
};
|
|
25
|
+
emit: ((evt: "update:selected", value: T[]) => void) & ((evt: "row-click", item: T) => void);
|
|
26
|
+
}>) => import("vue").VNode & {
|
|
27
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
28
|
+
};
|
|
29
|
+
export default _default;
|
|
30
|
+
type __VLS_PrettifyLocal<T> = {
|
|
31
|
+
[K in keyof T]: T[K];
|
|
32
|
+
} & {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
interface DialogState<TType extends string> {
|
|
3
|
+
type: TType;
|
|
4
|
+
data: unknown;
|
|
5
|
+
}
|
|
6
|
+
interface DialogManager<TType extends string> {
|
|
7
|
+
activeDialog: Ref<DialogState<TType> | undefined>;
|
|
8
|
+
openDialog: <T = void>(type: TType, data?: T) => void;
|
|
9
|
+
closeDialog: () => void;
|
|
10
|
+
isDialogOpen: (type: TType) => boolean;
|
|
11
|
+
getDialogData: <T>() => T | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function useDialogManager<TType extends string>(): DialogManager<TType>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
export function useDialogManager() {
|
|
3
|
+
const activeDialog = ref();
|
|
4
|
+
const openDialog = (type, data) => {
|
|
5
|
+
activeDialog.value = { type, data };
|
|
6
|
+
};
|
|
7
|
+
const closeDialog = () => {
|
|
8
|
+
activeDialog.value = void 0;
|
|
9
|
+
};
|
|
10
|
+
const isDialogOpen = (type) => {
|
|
11
|
+
return activeDialog.value?.type === type;
|
|
12
|
+
};
|
|
13
|
+
const getDialogData = () => {
|
|
14
|
+
return activeDialog.value?.data;
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
activeDialog,
|
|
18
|
+
openDialog,
|
|
19
|
+
closeDialog,
|
|
20
|
+
isDialogOpen,
|
|
21
|
+
getDialogData
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Caido } from "@caido/sdk-frontend";
|
|
2
|
+
import type { EditorView } from "@codemirror/view";
|
|
3
|
+
export declare function useFrontend(sdk: Caido): {
|
|
4
|
+
getEditorView: (showErrors?: boolean) => EditorView | undefined;
|
|
5
|
+
getEditorContent: () => string | undefined;
|
|
6
|
+
getSelectedText: () => string | undefined;
|
|
7
|
+
setEditorContent: (content: string, showErrors?: boolean) => boolean;
|
|
8
|
+
getReplayRequestId: () => string | undefined;
|
|
9
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export function useFrontend(sdk) {
|
|
2
|
+
const getEditorView = (showErrors = false) => {
|
|
3
|
+
const editor = sdk.window.getActiveEditor();
|
|
4
|
+
if (editor === void 0) {
|
|
5
|
+
if (showErrors) {
|
|
6
|
+
sdk.window.showToast("No active editor", { variant: "error" });
|
|
7
|
+
}
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
const view = editor.getEditorView();
|
|
11
|
+
if (view === void 0) {
|
|
12
|
+
if (showErrors) {
|
|
13
|
+
sdk.window.showToast("Could not access editor view", {
|
|
14
|
+
variant: "error"
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
return view;
|
|
20
|
+
};
|
|
21
|
+
const getEditorContent = () => {
|
|
22
|
+
const view = getEditorView();
|
|
23
|
+
if (view === void 0) return void 0;
|
|
24
|
+
return view.state.doc.toString();
|
|
25
|
+
};
|
|
26
|
+
const getSelectedText = () => {
|
|
27
|
+
const view = getEditorView();
|
|
28
|
+
if (view === void 0) return void 0;
|
|
29
|
+
const { from, to } = view.state.selection.main;
|
|
30
|
+
if (from === to) return void 0;
|
|
31
|
+
return view.state.sliceDoc(from, to);
|
|
32
|
+
};
|
|
33
|
+
const setEditorContent = (content, showErrors = false) => {
|
|
34
|
+
const view = getEditorView(showErrors);
|
|
35
|
+
if (view === void 0) return false;
|
|
36
|
+
view.dispatch({
|
|
37
|
+
changes: {
|
|
38
|
+
from: 0,
|
|
39
|
+
to: view.state.doc.length,
|
|
40
|
+
insert: content
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
45
|
+
const getReplayRequestId = () => {
|
|
46
|
+
const session = sdk.replay.getCurrentSession();
|
|
47
|
+
if (!session || session.entryIds.length === 0) {
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
const lastEntryId = session.entryIds[session.entryIds.length - 1];
|
|
51
|
+
if (lastEntryId === void 0) {
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
const entry = sdk.replay.getEntry(lastEntryId);
|
|
55
|
+
return entry?.requestId;
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
getEditorView,
|
|
59
|
+
getEditorContent,
|
|
60
|
+
getSelectedText,
|
|
61
|
+
setEditorContent,
|
|
62
|
+
getReplayRequestId
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CommandContext } from "@caido/sdk-frontend";
|
|
2
|
+
/**
|
|
3
|
+
* Get the request ID from a command context.
|
|
4
|
+
* Works with RequestContext (replay), RequestRowContext (http history),
|
|
5
|
+
* and ResponseContext.
|
|
6
|
+
* Throws if the context doesn't contain a request or the request hasn't been sent yet.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getRequestId(context: CommandContext): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function getRequestId(context) {
|
|
2
|
+
switch (context.type) {
|
|
3
|
+
case "RequestContext": {
|
|
4
|
+
if (!context.request.id) {
|
|
5
|
+
throw new Error("Request must be sent first");
|
|
6
|
+
}
|
|
7
|
+
return context.request.id;
|
|
8
|
+
}
|
|
9
|
+
case "RequestRowContext": {
|
|
10
|
+
if (context.requests.length === 0) {
|
|
11
|
+
throw new Error("No request selected");
|
|
12
|
+
}
|
|
13
|
+
return context.requests[0].id;
|
|
14
|
+
}
|
|
15
|
+
case "ResponseContext": {
|
|
16
|
+
return context.request.id;
|
|
17
|
+
}
|
|
18
|
+
default:
|
|
19
|
+
throw new Error("Can't get request ID from this context");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getRequestId } from "./ctx";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getRequestId } from "./ctx.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caido-utils/frontend",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"exports": {
|
|
9
|
+
"./utils": {
|
|
10
|
+
"types": "./dist/utils/index.d.ts",
|
|
11
|
+
"import": "./dist/utils/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./components": {
|
|
14
|
+
"types": "./dist/components/index.d.ts",
|
|
15
|
+
"import": "./dist/components/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./composables": {
|
|
18
|
+
"types": "./dist/composables/index.d.ts",
|
|
19
|
+
"import": "./dist/composables/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@caido/sdk-frontend": ">=0.54.0",
|
|
24
|
+
"@codemirror/view": ">=6.0.0",
|
|
25
|
+
"primevue": ">=4.0.0",
|
|
26
|
+
"vue": ">=3.4.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@caido/sdk-frontend": "0.54.2-beta.15",
|
|
30
|
+
"@codemirror/view": "6.28.1",
|
|
31
|
+
"primevue": "4.1.0",
|
|
32
|
+
"typescript": "^5.5.4",
|
|
33
|
+
"unbuild": "^3.6.1",
|
|
34
|
+
"vue": "3.4.37",
|
|
35
|
+
"vue-tsc": "^2.0.29"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "unbuild"
|
|
39
|
+
}
|
|
40
|
+
}
|