@viur/shop-components 0.0.1-dev.58 → 0.0.1-dev.60

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.
Files changed (86) hide show
  1. package/.editorconfig +16 -0
  2. package/.github/workflows/npm-publish.yml +42 -0
  3. package/.gitmodules +3 -0
  4. package/LICENSE +21 -0
  5. package/README.md +13 -2
  6. package/package.json +19 -32
  7. package/src/components/ShopCart.vue +512 -0
  8. package/src/components/ShopOrderComplete.vue +73 -0
  9. package/src/components/ShopOrderConfirm.vue +291 -0
  10. package/src/components/ShopOrderStepper.vue +264 -0
  11. package/src/components/ShopUserData.vue +232 -0
  12. package/src/components/cart/CartLeaf.vue +277 -0
  13. package/src/components/cart/CartLeafModel.vue +304 -0
  14. package/src/components/cart/CartNode.vue +25 -0
  15. package/src/components/cart/CartTree.vue +54 -0
  16. package/src/components/cart/CartTreeWrapper.vue +73 -0
  17. package/src/components/cart/CartView.vue +723 -0
  18. package/src/components/cart/Discount.vue +91 -0
  19. package/src/components/lib/utils.js +0 -0
  20. package/src/components/order/OrderSidebar.vue +102 -0
  21. package/src/components/order/category/CategoryList.vue +83 -0
  22. package/src/components/order/category/CategoryView.vue +143 -0
  23. package/src/components/order/information/adress/ShippingAdress.vue +143 -0
  24. package/src/components/order/item/ItemCard.vue +168 -0
  25. package/src/components/order/item/ItemView.vue +232 -0
  26. package/src/components/order/process/ConfirmView.vue +312 -0
  27. package/src/components/order/process/ExampleUsage.vue +113 -0
  28. package/src/components/order/process/OrderTabHeader.vue +16 -0
  29. package/src/components/order/process/SelectPaymentProvider.vue +62 -0
  30. package/src/components/order/process/Shipping.vue +46 -0
  31. package/src/components/ui/ShopSummary.vue +145 -0
  32. package/src/components/ui/generic/ArticleList.vue +222 -0
  33. package/src/components/ui/generic/ExamplePagination.vue +236 -0
  34. package/src/components/ui/generic/ShopPriceFormatter.vue +41 -0
  35. package/src/components/ui/generic/alerts/ShopAlert.vue +19 -0
  36. package/src/components/ui/generic/makeData.js +39 -0
  37. package/src/components/ui/stepper/StepperItem.vue +39 -0
  38. package/src/components/ui/stepper/StepperTab.vue +133 -0
  39. package/src/components/ui/stepper/StepperTrigger.vue +35 -0
  40. package/src/components/ui/userdata/AddForm.vue +125 -0
  41. package/src/components/ui/userdata/AddressBox.vue +117 -0
  42. package/src/components/ui/userdata/BaseLayout.vue +94 -0
  43. package/src/components/ui/userdata/CustomBooleanBone.vue +58 -0
  44. package/src/components/ui/userdata/CustomSelectBone.vue +91 -0
  45. package/src/components/ui/userdata/CustomStringBone.vue +71 -0
  46. package/src/components/ui/userdata/DefaultLayout.vue +126 -0
  47. package/src/components/ui/userdata/SelectAddress.vue +21 -0
  48. package/src/components/ui/userdata/multi/ActionBar.vue +38 -0
  49. package/src/components/ui/userdata/multi/CartSelection.vue +42 -0
  50. package/src/main.js +50 -0
  51. package/src/router/index.js +103 -0
  52. package/src/stores/cart.js +336 -0
  53. package/src/style/ignite/.editorconfig +20 -0
  54. package/src/style/ignite/.github/workflows/ignite.yml +64 -0
  55. package/src/style/ignite/.github/workflows/node.yml +30 -0
  56. package/src/style/ignite/.postcssrc.cjs +25 -0
  57. package/src/style/ignite/CHANGELOG.md +244 -0
  58. package/src/style/ignite/LICENSE +21 -0
  59. package/src/style/ignite/README.md +92 -0
  60. package/src/style/ignite/dist/ignite.css +2019 -0
  61. package/src/style/ignite/dist/ignite.min.css +4 -0
  62. package/src/style/ignite/foundation/basic.css +371 -0
  63. package/src/style/ignite/foundation/color.css +323 -0
  64. package/src/style/ignite/foundation/config.css +188 -0
  65. package/src/style/ignite/foundation/grid.css +78 -0
  66. package/src/style/ignite/foundation/mediaqueries.css +71 -0
  67. package/src/style/ignite/foundation/reset.css +261 -0
  68. package/src/style/ignite/ignite.css +29 -0
  69. package/src/style/ignite/ignite.css.map +1 -0
  70. package/src/style/ignite/package-lock.json +5530 -0
  71. package/src/style/ignite/package.json +58 -0
  72. package/src/style/ignite/shoelace.css +19 -0
  73. package/src/style/ignite/themes/dark.css +12 -0
  74. package/src/style/ignite/themes/light.css +11 -0
  75. package/src/style/ignite/utilities/shoelace.css +537 -0
  76. package/src/style/ignite/utilities/utilities.css +24 -0
  77. package/src/views/ViewMissing.vue +20 -0
  78. package/vite.config.js +53 -0
  79. package/dist/CategoryView-Z-tCFNv1.mjs +0 -60
  80. package/dist/ItemCard-CVfih_bz.mjs +0 -64
  81. package/dist/ItemView-cjeQBSTR.mjs +0 -1848
  82. package/dist/ItemView.css +0 -1
  83. package/dist/main-CZfMYx-A.mjs +0 -3167
  84. package/dist/main.css +0 -1
  85. package/dist/viur-shop-components.es.js +0 -10
  86. package/dist/viur-shop-components.umd.js +0 -417
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <shop-order-stepper
3
+ :tabs="state.tabs"
4
+ @tabChange="handleTabs"
5
+ :sidebar="true"
6
+ :sidebar-bottom="false"
7
+ :show-discount="false"
8
+ >
9
+ <!-- customize slots -->
10
+ <!-- <template #main> The Order Stepper </template> -->
11
+ <!-- <template #trigger> Buttons for Stepper control </template> -->
12
+ <!-- <template #sidebar> the side/bottom bar </template> -->
13
+ </shop-order-stepper>
14
+ </template>
15
+
16
+ <script setup>
17
+ import { onBeforeMount, reactive, shallowRef, computed } from "vue";
18
+ import ShopOrderStepper from "../../ShopOrderStepper.vue";
19
+ import CartView from "../../cart/CartView.vue";
20
+ import ShopCart from "../../ShopCart.vue";
21
+ import ConfirmView from "../../ShopOrderConfirm.vue";
22
+ import ShopOrderComplete from "../../ShopOrderComplete.vue";
23
+ import ShopUserData from "../../ShopUserData.vue";
24
+ import { useCartStore } from "../../../stores/cart";
25
+
26
+ const cartStore = useCartStore();
27
+
28
+ const rootNode = computed(() =>
29
+ cartStore.state.basketRootNode.key ? cartStore.state.basketRootNode.key : "",
30
+ );
31
+ const state = reactive({
32
+ rootNode: {},
33
+ tabs: {
34
+ cart: {
35
+ component: shallowRef(CartView),
36
+ props: {
37
+ sidebar: true,
38
+ mode: "basket",
39
+ cartKey: rootNode, // cartKey (on initial call has to be a root node) is a required prop, make sure that cartStore.init() is called before cart is mounted
40
+ placeholder: "/static/partnerbereich/img/placeholder.svg",
41
+ standalone: false,
42
+ },
43
+ displayName: "Warenkorb",
44
+ icon: { name: "bag" },
45
+ position: 2,
46
+ disabled: false,
47
+ },
48
+ confirm: {
49
+ component: shallowRef(ConfirmView),
50
+ props: {
51
+ tabName: "userInfo",
52
+ },
53
+ displayName: "Bestellung prüfen",
54
+ icon: { name: "clipboard-check" },
55
+ position: 5,
56
+ disabled: false,
57
+ },
58
+ orderComplete: {
59
+ component: shallowRef(ShopOrderComplete),
60
+ props: {
61
+ redirectUrl: "http://localhost:8081",
62
+ additionalComponents: [
63
+ {
64
+ component: shallowRef(CartView),
65
+ props: {
66
+ sidebar: true,
67
+ mode: "basket",
68
+ cartKey: rootNode,
69
+ },
70
+ },
71
+ ],
72
+ },
73
+ displayName: "Bestellung Abgeschlossen",
74
+ icon: { name: "bag-check" },
75
+ position: 6,
76
+ disabled: true,
77
+ },
78
+ userInfo: {
79
+ component: shallowRef(ShopUserData),
80
+ props: { multiMode: false },
81
+ displayName: "Daten Eingeben",
82
+ icon: { name: "card-list" },
83
+ position: 3,
84
+ disabled: false,
85
+ },
86
+ cartTest: {
87
+ component: shallowRef(ShopCart),
88
+ props: {
89
+ sidebar: true,
90
+ mode: "basket",
91
+ cartKey: rootNode, // cartKey (on initial call has to be a root node) is a required prop, make sure that cartStore.init() is called before cart is mounted
92
+ placeholder: "/static/partnerbereich/img/placeholder.svg",
93
+ standalone: false,
94
+ },
95
+ displayName: "neuer Korb",
96
+ icon: { name: "bag" },
97
+ position: 1,
98
+ disabled: false,
99
+ },
100
+ },
101
+ });
102
+
103
+ function handleTabs(e) {
104
+ if (e?.detail.name === "confirm") {
105
+ state.tabs.orderComplete.disabled = false;
106
+ }
107
+ }
108
+
109
+ onBeforeMount(async () => {
110
+ await cartStore.init("/static/partnerbereich/img/placeholder.svg");
111
+ await cartStore.getAddressStructure();
112
+ });
113
+ </script>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ HALLO
3
+ <div v-if="a">{{ a }}</div>
4
+ <div v-if="b">{{ b }}</div>
5
+ </template>
6
+
7
+ <script setup>
8
+ const props = defineProps({
9
+ a: {
10
+ type: String,
11
+ },
12
+ b: {
13
+ type: String,
14
+ },
15
+ });
16
+ </script>
@@ -0,0 +1,62 @@
1
+ <template>
2
+
3
+ <div v-for="(providerData,providerName,i) in cartStore.state.paymentProviders">
4
+ <sl-card selectable :id="'povider__'+providerName" @sl-change="providerChanged" :selected="i===0">
5
+ <img
6
+ slot="image"
7
+ src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
8
+ alt="A kitten sits patiently between a terracotta pot and decorative grasses."
9
+ />
10
+ <div slot="footer">
11
+ {{ providerData?.title }}
12
+ <br>
13
+ {{ providerData?.descr }}
14
+ </div>
15
+ </sl-card>
16
+ </div>
17
+
18
+ </template>
19
+
20
+ <script setup>
21
+ //todo styling
22
+ // set image
23
+
24
+ import {onBeforeMount, reactive} from "vue";
25
+ import {useCartStore} from "../../../stores/cart";
26
+
27
+ const cartStore = useCartStore();
28
+
29
+ function providerChanged(e) {
30
+ if (e.target.selected) {
31
+ console.log( "a",cartStore.state.selectedPaymentProvider)
32
+ console.log( "b",cartStore.state.paymentProviders)
33
+
34
+
35
+ cartStore.state.selectedPaymentProviderName = e.target.id.replace("povider__", "")
36
+ cartStore.state.selectedPaymentProvider = cartStore.state.paymentProviders[e.target.id.replace("povider__", "")]
37
+ console.log(cartStore.state.selectedPaymentProvider)
38
+ document.querySelectorAll("sl-card").forEach((card) => {
39
+
40
+ if (card !== e.target) {
41
+ card.selected = false;
42
+ }
43
+ })
44
+
45
+ } else {//can't deselect now
46
+ e.target.selected = true;
47
+
48
+ }
49
+ console.log("provider changed", e)
50
+ }
51
+
52
+ onBeforeMount(async () => {
53
+ await cartStore.getPaymentProviders();
54
+ })
55
+ </script>
56
+
57
+ <style scoped>
58
+ /*todo select styling for sl-card*/
59
+ sl-card[selected]::part(base) {
60
+ border: 5px solid #39b200;
61
+ }
62
+ </style>
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <div class="viur-shop-cart-sidebar-info-line">
3
+
4
+ <span>Versandkosten </span>
5
+ <sl-format-number
6
+ type="currency"
7
+ currency="EUR"
8
+ :value="shippingCost"
9
+ lang="de">
10
+ </sl-format-number>
11
+
12
+ </div>
13
+ <div class="viur-shop-cart-sidebar-info-line">
14
+ <slot name="custom"></slot>
15
+ </div>
16
+
17
+ </template>
18
+ <script setup>
19
+ import {useCartStore} from "../../../stores/cart";
20
+ import {onBeforeMount, reactive,computed} from "vue";
21
+
22
+ const cartStore = useCartStore();
23
+ const shippingCost = computed(() => {
24
+ return cartStore.state.basketRootNode?.shipping?.dest.shipping_cost ? cartStore.state.basketRootNode?.shipping.dest.shipping_cost : 0
25
+ })
26
+ onBeforeMount(async () => {
27
+ await cartStore.init();
28
+ console.log("has shipping ? ",cartStore.state.basketRootNode.shipping);
29
+ });
30
+
31
+ </script>
32
+
33
+ <style scoped>
34
+ .viur-shop-cart-sidebar-info-line {
35
+ display: flex;
36
+ flex-direction: row;
37
+ flex-wrap: nowrap;
38
+ margin: var(--sl-spacing-2x-small) 0;
39
+
40
+
41
+
42
+ span {
43
+ margin-right: auto;
44
+ }
45
+ }
46
+ </style>
@@ -0,0 +1,145 @@
1
+ ,
2
+ <template>
3
+ <h2 class="viur-shop-cart-sidebar-headline headline">Zusammenfassung</h2>
4
+ <br />
5
+ <div class="viur-shop-cart-sidebar-info-line">
6
+ <span>Zwischensumme</span>
7
+
8
+ <sl-format-number
9
+ lang="de"
10
+ type="currency"
11
+ currency="EUR"
12
+ :value="state.node?.total"
13
+ >
14
+ </sl-format-number>
15
+ <br />
16
+ </div>
17
+ <div class="viur-shop-cart-sidebar-info-line">
18
+ <span>Rabatt</span>
19
+
20
+ <sl-format-number
21
+ lang="de"
22
+ type="currency"
23
+ currency="EUR"
24
+ :value="state.node?.discount ? state.node.discount : 0"
25
+ >
26
+ </sl-format-number>
27
+ </div>
28
+ <div class="viur-shop-cart-sidebar-info-line">
29
+ <Shipping ref="shipping">
30
+ <template #custom v-if="customShippingComponent">
31
+ <component :is="customShippingComponent"></component>
32
+ </template>
33
+ </Shipping>
34
+ </div>
35
+ <div class="viur-shop-cart-sidebar-info-line total">
36
+ <span>Gesamt:</span>
37
+ <sl-format-number
38
+ lang="de"
39
+ type="currency"
40
+ currency="EUR"
41
+ :value="totalPrice"
42
+ >
43
+ </sl-format-number>
44
+ <!-- TODO: Some project needs "VAT included" here -->
45
+ </div>
46
+ <slot name="action-buttons">
47
+ HIER KANN DEIN BUTTON REIN
48
+ <div class="viur-shop-cart-sidebar-btn-wrap">
49
+ <!-- TODO: Placement of discount? -->
50
+ <div class="viur-shop-discount-wrap" v-if="showDiscount">
51
+ <Discount></Discount>
52
+ </div>
53
+
54
+ <sl-button variant="primary" size="medium"> Jetzt Bestellen</sl-button>
55
+ <sl-button size="medium" variant="info">
56
+ <sl-icon name="paypal" slot="prefix"></sl-icon>
57
+ Paypal
58
+ </sl-button>
59
+ </div>
60
+ <!-- TODO: Delivery time estimate: slot -->
61
+ </slot>
62
+ <slot name="custom"></slot>
63
+ </template>
64
+
65
+ <script setup>
66
+ import Discount from "../cart/Discount.vue";
67
+ import Shipping from "../order/process/Shipping.vue";
68
+ import { computed, onBeforeMount, reactive, ref } from "vue";
69
+ import { useCartStore } from "../../stores/cart";
70
+
71
+ const cartStore = useCartStore();
72
+
73
+ const shipping = ref(null);
74
+ const props = defineProps({
75
+ showDiscount: {type: Boolean, default: true},
76
+ customShippingComponent:{type:Object,default:undefined},
77
+ });
78
+ console.log("csc",props.customShippingComponent)
79
+ const state=reactive({
80
+ node:{}
81
+ })
82
+
83
+ const totalPrice = computed(() => {
84
+ if (state.node?.shipping) {
85
+ return (
86
+ state.node.total +
87
+ state.node?.shipping?.dest.shipping_cost -
88
+ state.node.discount
89
+ );
90
+ } else {
91
+ // use shippingprice formbasket
92
+ console.log(cartStore.state.basket);
93
+
94
+ if (cartStore.state.basket.length === 1) {
95
+ return (
96
+ state.node?.total +
97
+ cartStore.state.basket.shipping?.dest.shipping_cost -
98
+ state.node.discount
99
+ );
100
+ } else {
101
+ return state.node.total + 0 - state.node.discount; //kein shipping
102
+ }
103
+ }
104
+ });
105
+ onBeforeMount(async () => {
106
+ await cartStore.init();
107
+ state.node = cartStore.state.basketRootNode;
108
+ console.log("get node",state.node)
109
+ })
110
+ </script>
111
+
112
+ <style>
113
+ .viur-shop-cart-sidebar-info-line {
114
+ display: flex;
115
+ flex-direction: row;
116
+ flex-wrap: nowrap;
117
+ margin: var(--sl-spacing-2x-small) 0;
118
+
119
+ &.total {
120
+ font-weight: 600;
121
+ border-top: 1px solid var(--sl-color-neutral-300);
122
+ border-bottom: 1px solid var(--sl-color-neutral-300);
123
+ padding: var(--sl-spacing-x-small) 0;
124
+ margin: var(--sl-spacing-small) 0;
125
+ }
126
+
127
+ span {
128
+ margin-right: auto;
129
+ }
130
+ }
131
+
132
+ .viur-shop-cart-sidebar-btn-wrap {
133
+ display: flex;
134
+ flex-direction: column;
135
+ margin-top: var(--sl-spacing-large);
136
+
137
+ sl-button {
138
+ margin-bottom: var(--sl-spacing-x-small);
139
+ }
140
+ }
141
+
142
+ .viur-shop-cart-sidebar-headline {
143
+ margin: 0 0 var(--sl-spacing-large) 0;
144
+ }
145
+ </style>
@@ -0,0 +1,222 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ FlexRender,
4
+ createColumnHelper,
5
+ getCoreRowModel,
6
+ getExpandedRowModel,
7
+ useVueTable,
8
+ type ExpandedState,
9
+ type Row,
10
+ } from '@tanstack/vue-table'
11
+ import { Text, h, ref } from 'vue'
12
+ type Person = {
13
+ firstName: string
14
+ lastName: string
15
+ age: number
16
+ visits: number
17
+ status: string
18
+ progress: number
19
+ }
20
+ const defaultData: Person[] = [
21
+ {
22
+ firstName: 'tanner',
23
+ lastName: 'linsley',
24
+ age: 24,
25
+ visits: 100,
26
+ status: 'In Relationship',
27
+ progress: 50,
28
+ },
29
+ {
30
+ firstName: 'tandy',
31
+ lastName: 'miller',
32
+ age: 40,
33
+ visits: 40,
34
+ status: 'Single',
35
+ progress: 80,
36
+ },
37
+ {
38
+ firstName: 'joe',
39
+ lastName: 'dirte',
40
+ age: 45,
41
+ visits: 20,
42
+ status: 'Complicated',
43
+ progress: 10,
44
+ },
45
+ ]
46
+ const columnHelper = createColumnHelper<Person>()
47
+ function renderExpanded(row: Row<Person>) {
48
+ if (!row.getCanExpand()) {
49
+ return h(Text, '🔵')
50
+ }
51
+ return h(
52
+ 'button',
53
+ {
54
+ onClick: row.getToggleExpandedHandler(),
55
+ style: { cursor: 'pointer' },
56
+ },
57
+ row.getIsExpanded() ? '👇' : '👉'
58
+ )
59
+ }
60
+ const columns = [
61
+ columnHelper.group({
62
+ header: 'Name',
63
+ footer: props => props.column.id,
64
+ columns: [
65
+ columnHelper.display({
66
+ id: 'expander',
67
+ header: () => null,
68
+ cell: ({ row }) => renderExpanded(row),
69
+ }),
70
+ columnHelper.accessor('firstName', {
71
+ footer: props => props.column.id,
72
+ }),
73
+ columnHelper.accessor(row => row.lastName, {
74
+ id: 'lastName',
75
+ cell: info => info.getValue(),
76
+ header: () => 'Last Name',
77
+ footer: props => props.column.id,
78
+ }),
79
+ ],
80
+ }),
81
+ columnHelper.group({
82
+ header: 'Info',
83
+ footer: props => props.column.id,
84
+ columns: [
85
+ columnHelper.accessor('age', {
86
+ header: () => 'Age',
87
+ footer: props => props.column.id,
88
+ }),
89
+ columnHelper.group({
90
+ header: 'More Info',
91
+ columns: [
92
+ columnHelper.accessor('visits', {
93
+ header: () => 'Visits',
94
+ footer: props => props.column.id,
95
+ }),
96
+ columnHelper.accessor('status', {
97
+ header: 'Status',
98
+ footer: props => props.column.id,
99
+ }),
100
+ columnHelper.accessor('progress', {
101
+ header: 'Profile Progress',
102
+ footer: props => props.column.id,
103
+ }),
104
+ ],
105
+ }),
106
+ ],
107
+ }),
108
+ ]
109
+ const data = ref(defaultData)
110
+ const expanded = ref<ExpandedState>({})
111
+ const rerender = () => {
112
+ data.value = defaultData
113
+ }
114
+ const table = useVueTable({
115
+ get data() {
116
+ return data.value
117
+ },
118
+ state: {
119
+ get expanded() {
120
+ return expanded.value
121
+ },
122
+ },
123
+ columns,
124
+ getRowCanExpand: () => true,
125
+ getCoreRowModel: getCoreRowModel(),
126
+ getExpandedRowModel: getExpandedRowModel(),
127
+ onExpandedChange: updaterOrValue => {
128
+ expanded.value =
129
+ typeof updaterOrValue === 'function'
130
+ ? updaterOrValue(expanded.value)
131
+ : updaterOrValue
132
+ },
133
+ })
134
+ </script>
135
+
136
+ <template>
137
+ <div class="p-2">
138
+ <table>
139
+ <thead>
140
+ <tr
141
+ v-for="headerGroup in table.getHeaderGroups()"
142
+ :key="headerGroup.id"
143
+ >
144
+ <th
145
+ v-for="header in headerGroup.headers"
146
+ :key="header.id"
147
+ :colSpan="header.colSpan"
148
+ >
149
+ <FlexRender
150
+ v-if="!header.isPlaceholder"
151
+ :render="header.column.columnDef.header"
152
+ :props="header.getContext()"
153
+ />
154
+ </th>
155
+ </tr>
156
+ </thead>
157
+ <tbody>
158
+ <template v-for="row in table.getRowModel().rows" :key="row.id">
159
+ <tr>
160
+ <td v-for="cell in row.getVisibleCells()" :key="cell.id">
161
+ <FlexRender
162
+ :render="cell.column.columnDef.cell"
163
+ :props="cell.getContext()"
164
+ />
165
+ </td>
166
+ </tr>
167
+ <tr v-if="row.getIsExpanded()">
168
+ <td :colspan="row.getAllCells().length">
169
+ <pre :style="{ fontSize: '10px' }">
170
+ <code>{{ JSON.stringify(row.original, null, 2) }}</code>
171
+ </pre>
172
+ </td>
173
+ </tr>
174
+ </template>
175
+ </tbody>
176
+ <tfoot>
177
+ <tr
178
+ v-for="footerGroup in table.getFooterGroups()"
179
+ :key="footerGroup.id"
180
+ >
181
+ <th
182
+ v-for="header in footerGroup.headers"
183
+ :key="header.id"
184
+ :colSpan="header.colSpan"
185
+ >
186
+ <FlexRender
187
+ v-if="!header.isPlaceholder"
188
+ :render="header.column.columnDef.footer"
189
+ :props="header.getContext()"
190
+ />
191
+ </th>
192
+ </tr>
193
+ </tfoot>
194
+ </table>
195
+ <div class="h-4" />
196
+ <button @click="rerender" class="border p-2">Rerender</button>
197
+ </div>
198
+ </template>
199
+
200
+ <style>
201
+ html {
202
+ font-family: sans-serif;
203
+ font-size: 14px;
204
+ }
205
+ table {
206
+ border: 1px solid lightgray;
207
+ }
208
+ tbody {
209
+ border-bottom: 1px solid lightgray;
210
+ }
211
+ th {
212
+ border-bottom: 1px solid lightgray;
213
+ border-right: 1px solid lightgray;
214
+ padding: 2px 4px;
215
+ }
216
+ tfoot {
217
+ color: gray;
218
+ }
219
+ tfoot th {
220
+ font-weight: normal;
221
+ }
222
+ </style>