Skip to content

Commit 703652b

Browse files
committed
feat: pagination content table
1 parent 8eb7215 commit 703652b

File tree

8 files changed

+180
-165
lines changed

8 files changed

+180
-165
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
"dev:vue": "yarn p:vue dev",
2828
"dev:nuxt": "yarn p:nuxt dev",
2929
"build": "yarn workspaces run build",
30+
"build:helpers": "yarn p:enums build && yarn p:helpers build",
3031
"build:nuxt": "yarn p:vue build && yarn p:nuxt build",
32+
"build:js": "yarn build:helpers && yarn build:nuxt",
3133
"build:css": "yarn p:styles build",
3234
"release": "yarn workspaces run semantic-release",
3335
"release:dry": "yarn workspaces run semantic-release --dry-run"
@@ -64,7 +66,7 @@
6466
"stylelint-config-standard-scss": "^13.1.0",
6567
"stylelint-prettier": "^5.0.0",
6668
"typescript": "5.8.2",
67-
"vite": "^5.2.12",
69+
"vite": "^7.0.4",
6870
"vue-tsc": "2.1.10"
6971
},
7072
"husky": {

packages/common-enums/src/components.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44
export const componentNames = [
55
// base
6+
"BaseBrowserOnly",
67
"BaseImg",
78
"BaseAction",
89
"BaseInput",
@@ -41,6 +42,7 @@ export const componentNames = [
4142
// pagination
4243
"Pagination",
4344
"PaginationContent",
45+
"PaginationContentTable",
4446
// form
4547
"FormInput",
4648
"Form",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<template>
2+
<slot v-if="isBrowser"></slot>
3+
<slot v-else-if="$slots.fallback" name="fallback"></slot>
4+
</template>
5+
<script setup lang="ts">
6+
import { useUtils } from "@open-xamu-co/ui-common-helpers";
7+
8+
import { useHelpers } from "../../composables/utils";
9+
10+
const { isBrowser } = useHelpers(useUtils);
11+
</script>

packages/components-vue/src/components/loader/ContentFetch.vue

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
<BaseErrorBoundary :theme="theme">
33
<LoaderContent
44
v-bind="{
5-
content:
6-
!!content && patchedIsContent(content) && (!!fallback || firstLoad || hydrated),
5+
content: patchedIsContent(content) && (!!fallback || firstLoad || hydrated),
76
errors,
87
loading,
98
refresh,
@@ -18,9 +17,7 @@
1817
:class="$attrs.class"
1918
>
2019
<slot
21-
v-if="
22-
!!content && patchedIsContent(content) && (!!fallback || firstLoad || hydrated)
23-
"
20+
v-if="patchedIsContent(content) && (!!fallback || firstLoad || hydrated)"
2421
v-bind="{ content, refresh, loading, errors }"
2522
></slot>
2623
</LoaderContent>
@@ -198,8 +195,9 @@
198195
},
199196
});
200197
201-
function patchedIsContent(c?: T): boolean {
202-
return props.isContent?.(c) ?? !!c;
198+
function patchedIsContent(c?: T | null): c is NonNullable<T> {
199+
// isContent needs to run always
200+
return props.isContent?.(c ?? undefined) ?? !!c;
203201
}
204202
function validatePromiseLike(newPromise: any, oldPromise: any) {
205203
/**
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { Meta, StoryObj } from "@storybook/vue3";
2+
3+
import ContentTable from "./ContentTable.vue";
4+
5+
const meta = {
6+
title: "Pagination/Pagination ContentTable",
7+
component: ContentTable as Record<keyof typeof ContentTable, unknown>,
8+
args: {},
9+
} satisfies Meta<typeof ContentTable>;
10+
11+
type Story = StoryObj<typeof ContentTable>;
12+
13+
export const Sample: Story = {
14+
args: {},
15+
};
16+
17+
export default meta;
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<template>
2+
<BaseErrorBoundary>
3+
<slot v-bind="{ hasContent, refreshData }"></slot>
4+
<div class="flx --flxColumn --gap-30">
5+
<div
6+
v-if="!hasContent && $slots.headActions"
7+
class="flx --flxRow --flx-start-center --gap-10 --gap:md"
8+
>
9+
<slot name="headActions" v-bind="{ hasContent, refreshData }"></slot>
10+
</div>
11+
<PaginationContent
12+
v-slot="{ content }"
13+
v-bind="{ page, url, noContentMessage, preventAutoload, theme, client }"
14+
with-route
15+
:defaults="{ page: true, ...defaults }"
16+
class="flx --flxColumn --flx-start-end"
17+
@refresh="emittedRefresh = $event"
18+
@has-content="hasContent = $event"
19+
>
20+
<!-- Prevent hydration issues, data is fetched no matter what -->
21+
<BrowserOnly>
22+
<Table
23+
:nodes="mapNodes(content)"
24+
:refresh="refreshData"
25+
:class="tableClass"
26+
v-bind="{
27+
...tableProps,
28+
theme,
29+
modalProps: {
30+
invertTheme: true,
31+
class: modalClass ?? tableClass,
32+
...tableProps?.modalProps,
33+
},
34+
}"
35+
>
36+
<template v-if="$slots.headActions" #headActions>
37+
<div class="flx --flxRow --flx-start-center --gap-10 --gap:md">
38+
<slot
39+
name="headActions"
40+
v-bind="{ hasContent, refreshData }"
41+
></slot>
42+
</div>
43+
</template>
44+
<template v-if="$slots.tableChildren" #default="tableChildrenScope">
45+
<slot
46+
name="tableChildren"
47+
v-bind="{ ...tableChildrenScope, hasContent, refreshData }"
48+
></slot>
49+
</template>
50+
</Table>
51+
</BrowserOnly>
52+
</PaginationContent>
53+
</div>
54+
</BaseErrorBoundary>
55+
</template>
56+
57+
<script setup lang="ts" generic="T extends Record<string, any>, TM extends Record<string, any>">
58+
import { ref } from "vue";
59+
60+
import type { iGetPage, tThemeModifier, tThemeTuple } from "@open-xamu-co/ui-common-types";
61+
import type { iTableProps } from "@open-xamu-co/ui-components-vue";
62+
63+
import BaseErrorBoundary from "../base/ErrorBoundary.vue";
64+
import BrowserOnly from "../base/BrowserOnly.vue";
65+
import Table from "../table/Simple.vue";
66+
import PaginationContent from "./Content.vue";
67+
68+
export interface iPaginationContentTableProps<
69+
Ti extends Record<string, any>,
70+
TMi extends Record<string, any>,
71+
> {
72+
/**
73+
* Required to dedupe caching
74+
*/
75+
url: string;
76+
page: iGetPage<Ti>;
77+
defaults?: Record<string, any>;
78+
mapNode?: (node: Ti) => TMi;
79+
preventAutoload?: boolean;
80+
/**
81+
* Additional refresh function
82+
*/
83+
refresh?: () => void;
84+
noContentMessage?: string;
85+
tableProps?: Omit<iTableProps<TMi>, "nodes" | "refresh">;
86+
theme?: tThemeModifier | tThemeTuple;
87+
client?: boolean;
88+
/**
89+
* Additional class for the table
90+
*
91+
* @example --txtColor
92+
*/
93+
tableClass?: string | string[] | Record<string, boolean>;
94+
/**
95+
* Additional class for the modal
96+
*
97+
* @example --txtColor
98+
*/
99+
modalClass?: string | string[] | Record<string, boolean>;
100+
}
101+
102+
/**
103+
* Pagination Table
104+
* Showcase a table with pagination and data fetching
105+
*
106+
* @component
107+
*/
108+
109+
const props = withDefaults(defineProps<iPaginationContentTableProps<T, TM>>(), {
110+
mapNode: (node: T) => node as unknown as TM,
111+
});
112+
113+
const hasContent = ref(false);
114+
const emittedRefresh = ref();
115+
116+
function refreshData() {
117+
props.refresh?.();
118+
emittedRefresh.value?.();
119+
}
120+
121+
function mapNodes(nodes: T[] = []): TM[] {
122+
return nodes.map(props.mapNode);
123+
}
124+
</script>

packages/components-vue/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from "./types/props";
22

33
// base
4+
export { default as BaseBrowserOnly } from "./components/base/BrowserOnly.vue";
45
export { default as BaseImg } from "./components/base/Img.vue";
56
export { default as BaseAction } from "./components/base/Action.vue";
67
export { default as BaseInput } from "./components/base/Input.vue";
@@ -47,6 +48,7 @@ export { default as LoaderContentFetch } from "./components/loader/ContentFetch.
4748
// pagination
4849
export { default as Pagination } from "./components/pagination/Simple.vue";
4950
export { default as PaginationContent } from "./components/pagination/Content.vue";
51+
export { default as PaginationContentTable } from "./components/pagination/ContentTable.vue";
5052

5153
// form
5254
export { default as FormInput } from "./components/form/Input.vue";

0 commit comments

Comments
 (0)