From 1e408e9f043914dd25735433bcef070c079addf4 Mon Sep 17 00:00:00 2001 From: Moritz Vetter <16950410+Isokaeder@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:01:00 +0200 Subject: [PATCH] feature(KtTable): initial implementation Co-Authored-By: Florian Wendelborn <1133858+FlorianWendelborn@users.noreply.github.com> Co-Authored-By: Santiago Bellardes <744091+santiagoballadares@users.noreply.github.com> --- eslint.config.mjs | 1 + ...{CodePreview.vue => CodePreviewLegacy.vue} | 2 +- packages/documentation/data/menu.ts | 2 + packages/documentation/package.json | 1 + .../pages/usage/components/drawer.vue | 8 +- .../pages/usage/components/table-legacy.vue | 72 +- .../pages/usage/components/table.vue | 520 ++++++++++++++ packages/documentation/styles/tables.scss | 2 +- packages/eslint-config/source/index.ts | 2 + packages/kotti-ui/package.json | 2 + packages/kotti-ui/source/globals.d.ts | 18 + packages/kotti-ui/source/index.ts | 3 + .../kotti-field-currency/KtFieldCurrency.vue | 2 +- .../kotti-field-currency/utilities.test.ts | 2 +- .../source/kotti-field-currency/utilities.ts | 2 +- .../source/kotti-field-number/constants.ts | 2 +- .../source/kotti-field-number/utilities.ts | 2 +- .../kotti-field-toggle/KtFieldToggle.vue | 24 +- .../kotti-field-toggle/KtFieldToggleGroup.vue | 24 +- packages/kotti-ui/source/kotti-field/types.ts | 2 +- packages/kotti-ui/source/kotti-i18n/hooks.ts | 9 +- .../source/kotti-i18n/locales/de-DE.ts | 5 + .../source/kotti-i18n/locales/en-US.ts | 5 + .../source/kotti-i18n/locales/es-ES.ts | 5 + .../source/kotti-i18n/locales/fr-FR.ts | 5 + .../source/kotti-i18n/locales/ja-JP.ts | 5 + packages/kotti-ui/source/kotti-i18n/types.ts | 21 +- .../kotti-ui/source/kotti-table/KtTable.vue | 651 +++++++++++++++++ packages/kotti-ui/source/kotti-table/index.ts | 54 ++ .../source/kotti-table/table/column-helper.ts | 185 +++++ .../source/kotti-table/table/context.ts | 49 ++ .../source/kotti-table/table/hooks.ts | 652 ++++++++++++++++++ .../source/kotti-table/table/local.ts | 127 ++++ .../kotti-ui/source/kotti-table/table/row.ts | 50 ++ .../kotti-table/table/tanstack-table/index.ts | 123 ++++ .../source/kotti-table/table/types.ts | 142 ++++ .../kotti-table/table/use-computed-ref.ts | 46 ++ packages/kotti-ui/source/locales/input.json | 5 + .../toggle-inner}/ToggleBox.vue | 0 .../toggle-inner}/ToggleInner.vue | 24 +- .../toggle-inner}/ToggleSwitch.vue | 0 packages/kotti-ui/source/types/kotti.ts | 1 + packages/kotti-ui/source/utilities.ts | 2 +- packages/kotti-ui/tsconfig.json | 4 +- yarn.lock | 10 + 45 files changed, 2777 insertions(+), 96 deletions(-) rename packages/documentation/components/{CodePreview.vue => CodePreviewLegacy.vue} (97%) create mode 100644 packages/documentation/pages/usage/components/table.vue create mode 100644 packages/kotti-ui/source/kotti-table/KtTable.vue create mode 100644 packages/kotti-ui/source/kotti-table/index.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/column-helper.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/context.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/hooks.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/local.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/row.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/tanstack-table/index.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/types.ts create mode 100644 packages/kotti-ui/source/kotti-table/table/use-computed-ref.ts rename packages/kotti-ui/source/{kotti-field-toggle/components => shared-components/toggle-inner}/ToggleBox.vue (100%) rename packages/kotti-ui/source/{kotti-field-toggle/components => shared-components/toggle-inner}/ToggleInner.vue (70%) rename packages/kotti-ui/source/{kotti-field-toggle/components => shared-components/toggle-inner}/ToggleSwitch.vue (100%) diff --git a/eslint.config.mjs b/eslint.config.mjs index 3361c50f18..c01dcf96aa 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,7 @@ import yocoPackageJSON from './packages/yoco/package.json' assert { type: 'json' const trustedDependencies = new Set([ '@metatypes/typography', '@metatypes/units', + '@tanstack/table-core', 'filesize', 'nanoid', 'zod', diff --git a/packages/documentation/components/CodePreview.vue b/packages/documentation/components/CodePreviewLegacy.vue similarity index 97% rename from packages/documentation/components/CodePreview.vue rename to packages/documentation/components/CodePreviewLegacy.vue index 6400c5b0cf..687a0f3f39 100644 --- a/packages/documentation/components/CodePreview.vue +++ b/packages/documentation/components/CodePreviewLegacy.vue @@ -14,7 +14,7 @@ import { computed, defineComponent, ref } from 'vue' export default defineComponent({ - name: 'CodePreview', + name: 'CodePreviewLegacy', props: { vueSlotLabel: { default: 'Kotti-UI', type: String }, styleSlotLabel: { default: 'Kotti-Style', type: String }, diff --git a/packages/documentation/data/menu.ts b/packages/documentation/data/menu.ts index 999afe9dc0..ef58de8474 100644 --- a/packages/documentation/data/menu.ts +++ b/packages/documentation/data/menu.ts @@ -22,6 +22,7 @@ import { KtNavbar, KtPagination, KtPopover, + KtTable, KtTableLegacy, KtTag, KtToaster, @@ -173,6 +174,7 @@ export const menu: Array
= [ makeComponentMenuItem(KtModal), makeComponentMenuItem(KtPagination), makeComponentMenuItem(KtPopover), + makeComponentMenuItem(KtTable), makeComponentMenuItem(KtTableLegacy), makeComponentMenuItem(KtTag), makeComponentMenuItem(KtToaster), diff --git a/packages/documentation/package.json b/packages/documentation/package.json index 2186cbea6d..f6a53babe2 100644 --- a/packages/documentation/package.json +++ b/packages/documentation/package.json @@ -49,6 +49,7 @@ "fix:eslint": "yarn run check:eslint --fix", "fix:prettier": "yarn run check:prettier --write", "fix:stylelint": "yarn run check:stylelint --fix", + "test": "bun test", "watch": "nuxt" }, "version": "2.0.0" diff --git a/packages/documentation/pages/usage/components/drawer.vue b/packages/documentation/pages/usage/components/drawer.vue index 03a003d172..d5d2f95259 100644 --- a/packages/documentation/pages/usage/components/drawer.vue +++ b/packages/documentation/pages/usage/components/drawer.vue @@ -74,7 +74,7 @@ You can also customize the width of drawer by setting both `defaultWidth` and `e When the `disallowCloseOutside` flag is set, it prevents the user from accidentally closing the drawer by clicking outside of the drawer. - +
@@ -124,7 +124,7 @@ When the `disallowCloseOutside` flag is set, it prevents the user from accidenta
-
+ ## Usage @@ -143,14 +143,14 @@ import { defineComponent } from 'vue' import { KtDrawer } from '@3yourmind/kotti-ui' -import CodePreview from '~/components/CodePreview.vue' +import CodePreviewLegacy from '~/components/CodePreviewLegacy.vue' import ComponentInfo from '~/components/ComponentInfo.vue' export default defineComponent({ name: 'DocumentationPageUsageComponentsDrawer', components: { ComponentInfo, - CodePreview, + CodePreviewLegacy, }, data() { return { diff --git a/packages/documentation/pages/usage/components/table-legacy.vue b/packages/documentation/pages/usage/components/table-legacy.vue index 8f0da4b079..5ab8872e82 100644 --- a/packages/documentation/pages/usage/components/table-legacy.vue +++ b/packages/documentation/pages/usage/components/table-legacy.vue @@ -23,7 +23,7 @@ _Update:_ The use of `key` is deprecated use `prop` instead of `key` when defini _For better performance in complex operations_, define a `rowKey` to index each row with. - +
@@ -52,30 +52,12 @@ _For better performance in complex operations_, define a `rowKey` to index each
```html - - - - - - - - - - - - - - - - - - -
NameDateAddress
Tom2016-05-03No. 119, Grove St, Los Angeles
Jackson2016-05-02No. 89, Grove St, Los Angeles
+Mach ma so ```
- + ## Declarative form @@ -466,7 +448,7 @@ To sort remotly: > You must use `slot-scope` prop for the `actions` slot for it to be detected. > Update: shorthand for v-slot is used now, instead. - +
@@ -487,11 +469,11 @@ To sort remotly: ```
- + _Update_: Preferably, since the above syntax is now deprecated, use [v-slot](https://vuejs.org/v2/guide/components-slots.html) - +
```html @@ -514,13 +496,13 @@ _Update_: Preferably, since the above syntax is now deprecated, use [v-slot](htt ```
-
+ ## Expandable `isExpandable` enables expandability of the row**s**, defined on ``. You use the `slot="expand"` to define the template that shows on expansion. - +
@@ -542,13 +524,13 @@ _Update_: Preferably, since the above syntax is now deprecated, use [v-slot](htt ```
- + The default behavior only allows you to expand one row at a time; expanding one row would trigger any currently-expanded rows to shrink back. If you want to allow for the expansion of multiple rows at a time, set the `expandMultiple` flag on ``, as well. - +
@@ -569,7 +551,7 @@ If you want to allow for the expansion of multiple rows at a time, set the `expa ```
- + ## Custom Render @@ -590,7 +572,7 @@ It is possible to customize parts (columns) of the table by passing your own ren - `renderHeader` → custom render fn, to render a custom element in the header of the column. Instead you can use, slot='header' - `renderCell` → custom render fn, to render a custom element in the cells of the column. Instead use a default slot. - +
- + ```jsx { @@ -680,7 +662,7 @@ It is possible to customize parts (columns) of the table by passing your own ren } ``` - +
- + - +
- + ```js renderLoading() { @@ -750,7 +732,7 @@ renderEmpty() { You can also use slots instead of render props. [`slot="loading"`, `slot="empty"`, `slot="header"`, `slot="default"`]. - +
@@ -768,9 +750,9 @@ You can also use slots instead of render props. [`slot="loading"`, `slot="empty" ```
- + - +
@@ -791,9 +773,9 @@ You can also use slots instead of render props. [`slot="loading"`, `slot="empty" ```
- + - +
- + ### Provider/Consumer and Mixin @@ -862,7 +844,7 @@ _Notes_: > There’s also the _deprecated_ `KtTableColumnsStateMixin`. - +
@@ -949,7 +931,7 @@ _Notes_: ```
- + ```js { @@ -1112,7 +1094,7 @@ import { } from '@3yourmind/kotti-ui' import { Kotti } from '@3yourmind/kotti-ui' -import CodePreview from '~/components/CodePreview.vue' +import CodePreviewLegacy from '~/components/CodePreviewLegacy.vue' import ComponentInfo from '~/components/ComponentInfo.vue' import { info } from '~/utilities/toaster' @@ -1145,7 +1127,7 @@ const rows = [ export default { name: 'DocumentationPageUsageComponentsTable', components: { - CodePreview, + CodePreviewLegacy, ComponentInfo, KtAvatar, KtBanner, diff --git a/packages/documentation/pages/usage/components/table.vue b/packages/documentation/pages/usage/components/table.vue new file mode 100644 index 0000000000..1b08831f42 --- /dev/null +++ b/packages/documentation/pages/usage/components/table.vue @@ -0,0 +1,520 @@ + + + + + + + diff --git a/packages/documentation/styles/tables.scss b/packages/documentation/styles/tables.scss index dc5a3e15dd..d0eed067d8 100644 --- a/packages/documentation/styles/tables.scss +++ b/packages/documentation/styles/tables.scss @@ -1,6 +1,6 @@ @import '@3yourmind/kotti-ui/source/kotti-style/_variables.scss'; -table:not(.kt-table-legacy) { +*:not(.kt-table) > table:not(.kt-table-legacy) { width: 100%; hyphens: auto; table-layout: auto; diff --git a/packages/eslint-config/source/index.ts b/packages/eslint-config/source/index.ts index 14fdde4537..2523634d25 100644 --- a/packages/eslint-config/source/index.ts +++ b/packages/eslint-config/source/index.ts @@ -148,6 +148,8 @@ const baseConfig = tseslint.config({ 'sonarjs/no-duplicate-string': 'off', 'sonarjs/no-redundant-jump': 'off', 'sonarjs/no-small-switch': 'off', + 'sonarjs/no-use-of-empty-return-value': 'off', + // Unicorn 'unicorn/catch-error-name': 'warn', 'unicorn/consistent-destructuring': 'warn', diff --git a/packages/kotti-ui/package.json b/packages/kotti-ui/package.json index 640e7cf477..12aac46186 100644 --- a/packages/kotti-ui/package.json +++ b/packages/kotti-ui/package.json @@ -7,7 +7,9 @@ "@3yourmind/vue-use-tippy": "3.x", "@3yourmind/yoco": "^2.8.0", "@metatypes/typography": "^0.5.0", + "@tanstack/table-core": "^8.20.5", "big.js": "^6.2.1", + "classnames": "^2.5.1", "core-js": "3.6.5", "dayjs": "1.x", "deepmerge": "^4.3.1", diff --git a/packages/kotti-ui/source/globals.d.ts b/packages/kotti-ui/source/globals.d.ts index f7a099d53d..39cb270be4 100644 --- a/packages/kotti-ui/source/globals.d.ts +++ b/packages/kotti-ui/source/globals.d.ts @@ -1,5 +1,7 @@ import type { z } from 'zod' +import '@tanstack/table-core' + declare global { interface Window { /** @@ -9,3 +11,19 @@ declare global { lastZodSchema?: z.ZodTypeAny } } + +declare module '@tanstack/table-core' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface ColumnMeta { + cellClasses: Record | string + headerClasses: Record | string + type: + | 'boolean' + | 'custom' + | 'date' + | 'datetime' + | 'integer' + | 'numerical' + | 'text' + } +} diff --git a/packages/kotti-ui/source/index.ts b/packages/kotti-ui/source/index.ts index f93f1766b8..f94f919a93 100644 --- a/packages/kotti-ui/source/index.ts +++ b/packages/kotti-ui/source/index.ts @@ -91,6 +91,8 @@ import { KtPopover } from './kotti-popover' export * from './kotti-popover' import { KtRow } from './kotti-row' export * from './kotti-row' +import { KtTable } from './kotti-table' +export * from './kotti-table' import { KtTableLegacy, KtTableLegacyColumn, @@ -163,6 +165,7 @@ export default { KtPopover, KtRow, KtSplitButton, + KtTable, KtTableLegacy, KtTableLegacyColumn, KtTableLegacyConsumer, diff --git a/packages/kotti-ui/source/kotti-field-currency/KtFieldCurrency.vue b/packages/kotti-ui/source/kotti-field-currency/KtFieldCurrency.vue index 7dc2814241..54ff1ebe56 100644 --- a/packages/kotti-ui/source/kotti-field-currency/KtFieldCurrency.vue +++ b/packages/kotti-ui/source/kotti-field-currency/KtFieldCurrency.vue @@ -16,7 +16,7 @@ import { useField, useForceUpdate } from '../kotti-field/hooks' import { useI18nContext } from '../kotti-i18n/hooks' import type { KottiI18n } from '../kotti-i18n/types' import { makeProps } from '../make-props' -import { DecimalSeparator } from '../types/kotti' +import { DecimalSeparator } from '../types/decimal-separator' import { isNumberInRange } from '../utilities' import { KOTTI_FIELD_CURRENCY_SUPPORTS, VALID_REGEX } from './constants' diff --git a/packages/kotti-ui/source/kotti-field-currency/utilities.test.ts b/packages/kotti-ui/source/kotti-field-currency/utilities.test.ts index 5e1f5c70dd..d6adfa142b 100644 --- a/packages/kotti-ui/source/kotti-field-currency/utilities.test.ts +++ b/packages/kotti-ui/source/kotti-field-currency/utilities.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { DecimalSeparator } from '../types/kotti' +import { DecimalSeparator } from '../types/decimal-separator' import { VALID_REGEX } from './constants' import { toFixedPrecisionString } from './utilities' diff --git a/packages/kotti-ui/source/kotti-field-currency/utilities.ts b/packages/kotti-ui/source/kotti-field-currency/utilities.ts index 60c6266655..dc10d67853 100644 --- a/packages/kotti-ui/source/kotti-field-currency/utilities.ts +++ b/packages/kotti-ui/source/kotti-field-currency/utilities.ts @@ -1,6 +1,6 @@ import Big from 'big.js' -import type { DecimalSeparator } from '../types/kotti' +import type { DecimalSeparator } from '../types/decimal-separator' import { DECIMAL_SEPARATORS_CHARACTER_SET } from '../utilities' export const toNumber = ( diff --git a/packages/kotti-ui/source/kotti-field-number/constants.ts b/packages/kotti-ui/source/kotti-field-number/constants.ts index c764ce60c5..a86b090153 100644 --- a/packages/kotti-ui/source/kotti-field-number/constants.ts +++ b/packages/kotti-ui/source/kotti-field-number/constants.ts @@ -1,5 +1,5 @@ import type { KottiField } from '../kotti-field/types' -import { DecimalSeparator } from '../types/kotti' +import { DecimalSeparator } from '../types/decimal-separator' import { DECIMAL_SEPARATORS_CHARACTER_SET } from '../utilities' export const KOTTI_FIELD_NUMBER_SUPPORTS: KottiField.Supports = { diff --git a/packages/kotti-ui/source/kotti-field-number/utilities.ts b/packages/kotti-ui/source/kotti-field-number/utilities.ts index 61eb49352e..6f63bad2e0 100644 --- a/packages/kotti-ui/source/kotti-field-number/utilities.ts +++ b/packages/kotti-ui/source/kotti-field-number/utilities.ts @@ -1,6 +1,6 @@ import Big from 'big.js' -import type { DecimalSeparator } from '../types/kotti' +import type { DecimalSeparator } from '../types/decimal-separator' import { DECIMAL_SEPARATORS_CHARACTER_SET } from '../utilities' import { diff --git a/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggle.vue b/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggle.vue index 88077a213c..1db06beaf6 100644 --- a/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggle.vue +++ b/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggle.vue @@ -53,8 +53,8 @@ import FieldHelpText from '../kotti-field/components/FieldHelpText.vue' import { useField, useForceUpdate } from '../kotti-field/hooks' import { useTranslationNamespace } from '../kotti-i18n/hooks' import { makeProps } from '../make-props' +import ToggleInner from '../shared-components/toggle-inner/ToggleInner.vue' -import ToggleInner from './components/ToggleInner.vue' import { KOTTI_FIELD_TOGGLE_SUPPORTS } from './constants' import { KottiFieldToggle } from './types' @@ -138,5 +138,27 @@ export default defineComponent({ display: flex; align-items: center; } + + .kt-field-toggle-inner__svg { + flex-shrink: 0; + } + + .kt-field-toggle-inner__svg--is-box { + // align checkbox with the center of the first line of the label + // (assumption: font-size comes from common parent element) + // > starting point is upper end of the container (flex-start) + // > (+0.75em) Put upper edge of element into center (since line-height = 1.5 * font-size) + // > (-8px) Put it up half the height of the checkbox height (16px) + transform: translateY(calc(0.75em - 8px)); + } + + .kt-field-toggle-inner__svg--is-switch { + // align switch with the center of the first line of the label + // (assumption: font-size comes from common parent element) + // > starting point is upper end of the container (flex-start) + // > (+0.75em) Put upper edge of element into center (since line-height = 1.5 * font-size) + // > (-10px) Put it up half the height of the switch height (20px) + transform: translateY(calc(0.75em - 10px)); + } } diff --git a/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggleGroup.vue b/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggleGroup.vue index 89c08081fe..6d84f19314 100644 --- a/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggleGroup.vue +++ b/packages/kotti-ui/source/kotti-field-toggle/KtFieldToggleGroup.vue @@ -41,8 +41,8 @@ import { KtField } from '../kotti-field' import FieldHelpText from '../kotti-field/components/FieldHelpText.vue' import { useField, useForceUpdate } from '../kotti-field/hooks' import { makeProps } from '../make-props' +import ToggleInner from '../shared-components/toggle-inner/ToggleInner.vue' -import ToggleInner from './components/ToggleInner.vue' import { KOTTI_FIELD_TOGGLE_SUPPORTS } from './constants' import { KottiFieldToggleGroup } from './types' @@ -151,5 +151,27 @@ export default defineComponent({ &__content { font-size: var(--font-size-small); } + + .kt-field-toggle-inner__svg { + flex-shrink: 0; + } + + .kt-field-toggle-inner__svg--is-box { + // align checkbox with the center of the first line of the label + // (assumption: font-size comes from common parent element) + // > starting point is upper end of the container (flex-start) + // > (+0.75em) Put upper edge of element into center (since line-height = 1.5 * font-size) + // > (-8px) Put it up half the height of the checkbox height (16px) + transform: translateY(calc(0.75em - 8px)); + } + + .kt-field-toggle-inner__svg--is-switch { + // align switch with the center of the first line of the label + // (assumption: font-size comes from common parent element) + // > starting point is upper end of the container (flex-start) + // > (+0.75em) Put upper edge of element into center (since line-height = 1.5 * font-size) + // > (-10px) Put it up half the height of the switch height (20px) + transform: translateY(calc(0.75em - 10px)); + } } diff --git a/packages/kotti-ui/source/kotti-field/types.ts b/packages/kotti-ui/source/kotti-field/types.ts index c9d238896b..bdaace8ecb 100644 --- a/packages/kotti-ui/source/kotti-field/types.ts +++ b/packages/kotti-ui/source/kotti-field/types.ts @@ -91,7 +91,7 @@ export module KottiField { 'data-test': string disabled: boolean id: string - tabindex: KottiField.PropsInternal['tabIndex'] + tabindex: number }> isEmpty: boolean setValue(newValue: DATA_TYPE, options?: { forceUpdate: boolean }): void diff --git a/packages/kotti-ui/source/kotti-i18n/hooks.ts b/packages/kotti-ui/source/kotti-i18n/hooks.ts index 34ab4bc720..cfad02e275 100644 --- a/packages/kotti-ui/source/kotti-i18n/hooks.ts +++ b/packages/kotti-ui/source/kotti-i18n/hooks.ts @@ -2,7 +2,7 @@ import elementLocale from 'element-ui/lib/locale/index.js' import type { Ref, UnwrapRef } from 'vue' import { computed, inject, provide, reactive, watch } from 'vue' -import { DecimalSeparator } from '../types/kotti' +import { DecimalSeparator } from '../types/decimal-separator' import { KT_I18N_CONTEXT } from './constants' import { deDE } from './locales/de-DE' @@ -13,12 +13,7 @@ import { jaJP } from './locales/ja-JP' import type { KottiI18n } from './types' import { fixDeepMerge } from './utilities' -export const useI18nContext = (): { - currencyMap: KottiI18n.CurrencyMap - locale: KottiI18n.SupportedLanguages - messages: KottiI18n.Messages - numberFormat: KottiI18n.NumberFormat -} => { +export const useI18nContext = (): KottiI18n.ContextInternal => { const context = inject(KT_I18N_CONTEXT, null) if (context === null) diff --git a/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts b/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts index f80ebb5d57..7b38a3bb6d 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts @@ -133,6 +133,11 @@ export const deDE: KottiI18n.Messages = { menuExpand: 'Menü einblenden', quickLinksTitle: 'Schnellzugriff', }, + KtTable: { + no: 'Nein', + noItems: 'Keine Einträge', + yes: 'Ja', + }, KtValueLabel: { notSet: 'Nicht festgelegt', }, diff --git a/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts b/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts index 49f3d388ec..23eb9d6245 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts @@ -133,6 +133,11 @@ export const enUS: KottiI18n.Messages = { menuExpand: 'Expand menu', quickLinksTitle: 'Quick Links', }, + KtTable: { + no: 'No', + noItems: 'No Items', + yes: 'Yes', + }, KtValueLabel: { notSet: 'Not Set', }, diff --git a/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts b/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts index f6d19381f6..e3b6c10438 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts @@ -133,6 +133,11 @@ export const esES: KottiI18n.Messages = { menuExpand: 'Expandir el menú', quickLinksTitle: 'Enlaces rápidos', }, + KtTable: { + no: 'No', + noItems: 'No Data', + yes: 'Yes', + }, KtValueLabel: { notSet: 'No Establecido', }, diff --git a/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts b/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts index 603d6d627c..6269ca64d6 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts @@ -133,6 +133,11 @@ export const frFR: KottiI18n.Messages = { menuExpand: 'Étendre le menu', quickLinksTitle: 'Liens Rapides', }, + KtTable: { + no: 'No', + noItems: 'No Data', + yes: 'Yes', + }, KtValueLabel: { notSet: 'Non définie', }, diff --git a/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts b/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts index 9f2d47486e..aaa8b1ea1d 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts @@ -133,6 +133,11 @@ export const jaJP: KottiI18n.Messages = { menuExpand: '拡張メニュー', quickLinksTitle: 'クイックリンク', }, + KtTable: { + no: 'No', + noItems: 'No Data', + yes: 'Yes', + }, KtValueLabel: { notSet: '未設定', }, diff --git a/packages/kotti-ui/source/kotti-i18n/types.ts b/packages/kotti-ui/source/kotti-i18n/types.ts index 3668c3d9e4..532c75d682 100644 --- a/packages/kotti-ui/source/kotti-i18n/types.ts +++ b/packages/kotti-ui/source/kotti-i18n/types.ts @@ -1,4 +1,5 @@ import type { Ref } from 'vue' +import { z } from 'zod' import type { KottiComment } from '../kotti-comment/types' import type { KottiField } from '../kotti-field/types' @@ -8,8 +9,9 @@ import type { Shared as KottiFieldSelectShared } from '../kotti-field-select/typ import type { KottiFilters } from '../kotti-filters/types' import type { KottiFormSubmit } from '../kotti-form-submit/types' import type { KottiNavbar } from '../kotti-navbar/types' +import type { KottiTable } from '../kotti-table/table/types' import type { KottiValueLabel } from '../kotti-value-label/types' -import type { DecimalSeparator } from '../types/kotti' +import { DecimalSeparator } from '../types/decimal-separator' export type DeepPartial = T extends Record ? { [K in keyof T]?: DeepPartial } : T @@ -22,14 +24,24 @@ export module KottiI18n { numberFormat: Ref } + export type ContextInternal = { + currencyMap: CurrencyMap + locale: SupportedLanguages + messages: Messages + numberFormat: NumberFormat + } + export type CurrencyMap = Record< string, { decimalPlaces: number; symbol: string } > - export type NumberFormat = { - decimalSeparator: DecimalSeparator - } + export const numberFormatSchema = z + .object({ + decimalSeparator: z.nativeEnum(DecimalSeparator), + }) + .strict() + export type NumberFormat = z.output export type Messages = { KtComment: KottiComment.Translations @@ -40,6 +52,7 @@ export module KottiI18n { KtFilters: KottiFilters.Translations KtFormSubmit: KottiFormSubmit.Translations KtNavbar: KottiNavbar.Translations + KtTable: KottiTable.Translations KtValueLabel: KottiValueLabel.Translations } diff --git a/packages/kotti-ui/source/kotti-table/KtTable.vue b/packages/kotti-ui/source/kotti-table/KtTable.vue new file mode 100644 index 0000000000..bf69f19187 --- /dev/null +++ b/packages/kotti-ui/source/kotti-table/KtTable.vue @@ -0,0 +1,651 @@ + + + + + diff --git a/packages/kotti-ui/source/kotti-table/index.ts b/packages/kotti-ui/source/kotti-table/index.ts new file mode 100644 index 0000000000..77c606d09b --- /dev/null +++ b/packages/kotti-ui/source/kotti-table/index.ts @@ -0,0 +1,54 @@ +import type { Kotti } from '../types' +import { MetaDesignType } from '../types/kotti' +import { attachMeta, makeInstallable } from '../utilities' + +import KtTableVue from './KtTable.vue' +import { KottiTable } from './table/types' + +export { + createColumnContext, + getCustomDisplay, + getDisplay, +} from './table/column-helper' +export { useKottiTable } from './table/hooks' +export { getNumericalSorter, getTextSorter, useLocalSort } from './table/local' + +const TABLE_META: Kotti.Meta = { + addedVersion: '8.2.0', + deprecated: null, + designs: { + type: MetaDesignType.FIGMA, + url: 'https://www.figma.com/file/0yFVivSWXgFf2ddEF92zkf/Kotti-Design-System?node-id=128%3A0', + }, + slots: { + actions: { + description: 'E.g. edit/delete row actions', + scope: { + row: { description: 'Original row data', type: 'object' }, + rowIndex: { description: 'Position of row', type: 'integer' }, + }, + }, + empty: { + description: + 'Alternative to emptyText prop. Shown when the Table is empty', + scope: null, + }, + 'expanded-row': { + description: 'Per row, allows showing more info on-demand', + scope: { + row: { description: 'Original row data', type: 'object' }, + rowIndex: { description: 'Position of row', type: 'integer' }, + }, + }, + loading: { + description: 'Shown when loading', + scope: null, + }, + }, + typeScript: { + namespace: 'Kotti.Table', + schema: KottiTable.propsSchema, + }, +} + +export const KtTable = attachMeta(makeInstallable(KtTableVue), TABLE_META) diff --git a/packages/kotti-ui/source/kotti-table/table/column-helper.ts b/packages/kotti-ui/source/kotti-table/table/column-helper.ts new file mode 100644 index 0000000000..163dc25b63 --- /dev/null +++ b/packages/kotti-ui/source/kotti-table/table/column-helper.ts @@ -0,0 +1,185 @@ +import dayjs from 'dayjs' +import { h } from 'vue' + +import { Yoco } from '@3yourmind/yoco' + +import type { KottiTable } from './types' + +// TODO: truncate text, ask how default behavior +// TODO (nice-to-have): attachments, needs design +// TODO (nice-to-have): image, array of urls as data +// TODO (nice-to-have): tuples with separator (e.g. "1234 x 23") + +type DisplayOptionBoolean = { + mode: 'icon' | 'text' + type: 'boolean' +} + +type DisplayOptionDate = { + formatString: string + type: 'date' +} +type DisplayOptionDateTime = { + formatString: string + type: 'date-time' +} +type DisplayOptionInteger = { type: 'integer' } +type DisplayOptionNumerical = { + decimalPlaces?: number + type: 'numerical' +} +type DisplayOptionText = { type: 'text' } +type DisplayOption = + | DisplayOptionBoolean + | DisplayOptionDate + | DisplayOptionDateTime + | DisplayOptionInteger + | DisplayOptionNumerical + | DisplayOptionText + +type ResolvedDisplayType