@public-ui/sample-react 4.3.0-rc.3 → 4.3.0-rc.5

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.
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@public-ui/sample-react",
3
- "version": "4.3.0-rc.3",
3
+ "version": "4.3.0-rc.5",
4
4
  "description": "This app contains samples for the KoliBri/Public UI",
5
5
  "type": "module",
6
6
  "license": "EUPL-1.2",
@@ -27,9 +27,9 @@
27
27
  "typescript": "5.9.3",
28
28
  "world_countries_lists": "3.3.0",
29
29
  "zod": "4.4.3",
30
- "@public-ui/components": "4.3.0-rc.3",
31
- "@public-ui/react-v19": "4.3.0-rc.3",
32
- "@public-ui/react-hook-form-adapter": "4.3.0-rc.3"
30
+ "@public-ui/components": "4.3.0-rc.5",
31
+ "@public-ui/react-hook-form-adapter": "4.3.0-rc.5",
32
+ "@public-ui/react-v19": "4.3.0-rc.5"
33
33
  },
34
34
  "devDependencies": {
35
35
  "eslint": "9.39.4",
@@ -39,7 +39,7 @@
39
39
  "stylelint": "17.13.0"
40
40
  },
41
41
  "files": [
42
- "public",
42
+ "assets",
43
43
  "src",
44
44
  "tsconfig.json"
45
45
  ],
@@ -7,15 +7,11 @@ import type { FC } from 'react';
7
7
  export const AbbrBasic: FC = () => (
8
8
  <>
9
9
  <SampleDescription>
10
- <p>KolAbbr shows an abbreviation and uses kol-tooltip-wc to display the long form, if provided.</p>
10
+ <p>KolAbbr shows an abbreviation.</p>
11
11
  </SampleDescription>
12
12
 
13
13
  <p>
14
- I am <KolAbbr _label="as an example">e.g.</KolAbbr> an abbreviation.
15
- </p>
16
-
17
- <p>
18
- I am <KolAbbr>e.g.</KolAbbr> an abbreviation without label.
14
+ I am <KolAbbr>e.g.</KolAbbr> an abbreviation.
19
15
  </p>
20
16
  </>
21
17
  );
@@ -0,0 +1,27 @@
1
+ import { KolButton, KolLinkButton } from '@public-ui/react-v19';
2
+ import type { FC } from 'react';
3
+ import React from 'react';
4
+ import { useToasterService } from '../../hooks/useToasterService';
5
+ import { SampleDescription } from '../SampleDescription';
6
+
7
+ export const ButtonInFixedGrid: FC = () => {
8
+ const { dummyClickEventHandler } = useToasterService();
9
+
10
+ const dummyEventHandler = {
11
+ onClick: dummyClickEventHandler,
12
+ };
13
+
14
+ return (
15
+ <>
16
+ <SampleDescription>
17
+ <p>All this buttons are in a grid with fixed height (60px) and width (25% of screen) and should accept this dimensions.</p>
18
+ </SampleDescription>
19
+ <div className="grid gap-4 fixed-height-grid">
20
+ <KolButton _variant="primary" _icons="kolicon-house" _label="Button" _on={dummyEventHandler} />
21
+ <KolButton _variant="primary" _hideLabel _icons="kolicon-house" _label="Button" _on={dummyEventHandler} />
22
+ <KolLinkButton _variant="primary" _href="#" _icons="kolicon-house" _label="Link-Button" _on={dummyEventHandler} />
23
+ <KolLinkButton _variant="primary" _hideLabel _href="#" _icons="kolicon-house" _label="Link-Button" _on={dummyEventHandler} />
24
+ </div>
25
+ </>
26
+ );
27
+ };
@@ -3,6 +3,7 @@ import { ButtonAccessKey } from './access-key';
3
3
  import { ButtonAriaDescription } from './aria-description';
4
4
  import { ButtonBaselined } from './baselined';
5
5
  import { ButtonBasic } from './basic';
6
+ import { ButtonInFixedGrid } from './buttons-in-fixed-grid';
6
7
  import { ButtonDisabled } from './disabled';
7
8
  import { ButtonExpertSlot } from './expert-slot';
8
9
  import { ButtonFocusOptions } from './focus-options';
@@ -30,5 +31,6 @@ export const BUTTON_ROUTES: Routes = {
30
31
  'expert-slot': ButtonExpertSlot,
31
32
  'row-reverse-tooltip': ButtonRowReverseTooltip,
32
33
  'focus-options': ButtonFocusOptions,
34
+ 'fixed-grid': ButtonInFixedGrid,
33
35
  },
34
36
  };
@@ -1,10 +1,17 @@
1
1
  import { KolButton, KolHeading } from '@public-ui/react-v19';
2
2
  import type { FC } from 'react';
3
- import React from 'react';
3
+ import React, { useMemo } from 'react';
4
+ import { useSearchParams } from 'react-router-dom';
4
5
  import { useToasterService } from '../../hooks/useToasterService';
6
+ import { fetchVariantData } from '../../shares/fetchVariantData';
7
+ import { getCustomThemes } from '../../shares/store';
5
8
  import { SampleDescription } from '../SampleDescription';
6
9
 
7
10
  export const ButtonVariants: FC = () => {
11
+ const [searchParams] = useSearchParams();
12
+ const theme = searchParams.get('theme') ?? getCustomThemes()?.[0]?.key;
13
+ const data = useMemo(() => (theme ? fetchVariantData(theme, 'buttonVariants') : []), [theme]);
14
+
8
15
  const { dummyClickEventHandler } = useToasterService();
9
16
 
10
17
  const dummyEventHandler = {
@@ -14,19 +21,32 @@ export const ButtonVariants: FC = () => {
14
21
  return (
15
22
  <>
16
23
  <SampleDescription>
17
- <p>This story showcases all available button variants: primary, secondary, tertiary, normal, danger, and ghost.</p>
24
+ <p>
25
+ This story showcases all available button variants for this theme. You can import <code>ButtonVariantsEnum</code> from your theme to always use the
26
+ right variants.
27
+ </p>
18
28
  </SampleDescription>
19
29
 
20
30
  <div className="grid gap-8">
21
31
  <section className="grid gap-4">
22
- <KolHeading _level={2} _label="All Button Variants" />
23
- <div className="flex flex-wrap gap-4">
24
- <KolButton _icons="kolicon-house" _label="Primary" _variant="primary" _on={dummyEventHandler} />
25
- <KolButton _icons="kolicon-kolibri" _label="Secondary" _variant="secondary" _on={dummyEventHandler} />
26
- <KolButton _icons="kolicon-cogwheel" _label="Tertiary" _variant="tertiary" _on={dummyEventHandler} />
27
- <KolButton _icons="kolicon-cogwheel" _label="Normal" _variant="normal" _on={dummyEventHandler} />
28
- <KolButton _icons="kolicon-alert-warning" _label="Danger" _variant="danger" _on={dummyEventHandler} />
29
- <KolButton _icons="kolicon-eye-closed" _label="Ghost" _variant="ghost" _on={dummyEventHandler} />
32
+ <KolHeading _level={2} _label="All theme exclusive button variants" />
33
+ <div className="flex flex-wrap gap-4 items-center">
34
+ {!Array.isArray(data) || data.length === 0 ? (
35
+ <p>This theme has no variants for this component.</p>
36
+ ) : (
37
+ data.map((element) => {
38
+ return <KolButton _icons="kolicon-house" _label={`${element}`} _variant={element} key={element} _on={dummyEventHandler} />;
39
+ })
40
+ )}
41
+ </div>
42
+ <div className="flex flex-wrap gap-4 items-center">
43
+ {!Array.isArray(data) || data.length === 0 ? (
44
+ <p>This theme has no variants for this component.</p>
45
+ ) : (
46
+ data.map((element) => {
47
+ return <KolButton _hideLabel _icons="kolicon-settings" _label={`${element}`} _variant={element} key={element} _on={dummyEventHandler} />;
48
+ })
49
+ )}
30
50
  </div>
31
51
  </section>
32
52
  </div>
@@ -1,10 +1,12 @@
1
1
  import type { Routes } from '../../shares/types';
2
2
  import { DialogBasic } from './basic';
3
+ import { DialogScrollLock } from './scroll-lock';
3
4
  import { DialogWithAlert } from './with-alert';
4
5
 
5
6
  export const DIALOG_ROUTES: Routes = {
6
7
  dialog: {
7
8
  basic: DialogBasic,
9
+ 'scroll-lock': DialogScrollLock,
8
10
  'with-alert': DialogWithAlert,
9
11
  },
10
12
  };
@@ -0,0 +1,46 @@
1
+ import type { FC } from 'react';
2
+ import React, { useRef } from 'react';
3
+
4
+ import { KolButton, KolDialog, KolDrawer } from '@public-ui/react-v19';
5
+ import { SampleDescription } from '../SampleDescription';
6
+
7
+ export const DialogScrollLock: FC = () => {
8
+ const dialogRef = useRef<HTMLKolDialogElement>(null);
9
+ const drawerRef = useRef<HTMLKolDrawerElement>(null);
10
+
11
+ return (
12
+ <>
13
+ <SampleDescription>
14
+ <p>
15
+ Demonstrates that opening <strong>KolDialog</strong>/<strong>KolModal</strong> or <strong>KolDrawer</strong> modally locks the background scroll: the
16
+ page behind the overlay must not scroll, neither via mouse wheel/touch over the backdrop nor by dragging the page scrollbar. Non-modal{' '}
17
+ <code>show()</code> does not lock.
18
+ </p>
19
+ <p>
20
+ <strong>How to test:</strong> scroll this long page down a bit, open the dialog or drawer below, then try to scroll the background (mouse wheel over
21
+ the dimmed backdrop, or drag the scrollbar) – it should stay put. Close the overlay again and the page should scroll normally, restored to the same
22
+ position without a layout shift.
23
+ </p>
24
+ </SampleDescription>
25
+
26
+ <div className="flex flex-wrap gap-4 mb-4">
27
+ <KolButton _label="Open modal dialog" _on={{ onClick: () => dialogRef.current?.showModal() }} />
28
+ <KolButton _label="Open modal drawer" _on={{ onClick: () => drawerRef.current?.showModal() }} />
29
+ </div>
30
+
31
+ <KolDialog ref={dialogRef} _label="Scroll lock dialog" _variant="card" _width="30%">
32
+ <p className="mt-0">While I am open, the background must not scroll.</p>
33
+ <KolButton _label="Close" _on={{ onClick: () => dialogRef.current?.closeModal() }} />
34
+ </KolDialog>
35
+
36
+ <KolDrawer ref={drawerRef} _label="Scroll lock drawer" _align="right">
37
+ <p className="mt-0">While I am open, the background must not scroll.</p>
38
+ <KolButton _label="Close" _on={{ onClick: () => drawerRef.current?.close() }} />
39
+ </KolDrawer>
40
+
41
+ <div style={{ height: '200vh', flexShrink: 0 }} className="border border-dashed border-gray p-4">
42
+ <p>This tall block simulates a long page. Scroll down, open an overlay above, and try to scroll here.</p>
43
+ </div>
44
+ </>
45
+ );
46
+ };
@@ -242,11 +242,7 @@ export const HandoutBasic: FC = () => {
242
242
  <KolCard className="col-span-6 sm:col-span-6 md:col-span-3 xl:col-span-2" _label="Abbreviation and Progress" _level={2}>
243
243
  <div slot="" className="grid gap-2 p-2">
244
244
  <p>
245
- I am <KolAbbr _label="as an example">e.g.</KolAbbr> an abbreviation.
246
- </p>
247
-
248
- <p>
249
- I am <KolAbbr>e.g.</KolAbbr> an abbreviation without label.
245
+ I am <KolAbbr>e.g.</KolAbbr> an abbreviation.
250
246
  </p>
251
247
  <KolProgress _variant="bar" _max={100} _value={33} _label="Progress" />
252
248
  <KolProgress _variant="cycle" _max={100} _value={66} _label="Progress" />
@@ -455,7 +451,7 @@ export const HandoutBasic: FC = () => {
455
451
  <KolInputRadio _orientation="horizontal" _options="[{'label':'Mr.','value':0},{'label':'Mrs.','value':1}]" _value="0" _label={`Salutation`} />
456
452
  <KolInputCheckbox _label="">
457
453
  <span slot="expert">
458
- I accept the <KolAbbr _label="General Terms and Conditions">AGB</KolAbbr>.
454
+ I accept the <KolAbbr>AGB</KolAbbr>.
459
455
  </span>
460
456
  </KolInputCheckbox>
461
457
  </div>
@@ -1,28 +1,64 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
 
3
3
  import { KolLinkButton } from '@public-ui/react-v19';
4
4
 
5
5
  import type { FC } from 'react';
6
+ import { useSearchParams } from 'react-router-dom';
7
+ import { useToasterService } from '../../hooks/useToasterService';
8
+ import { fetchVariantData } from '../../shares/fetchVariantData';
9
+ import { getCustomThemes } from '../../shares/store';
6
10
  import { SampleDescription } from '../SampleDescription';
7
11
 
8
- const ARGS = {
9
- _href: '#/back-page',
10
- };
12
+ export const LinkButtonBasic: FC = () => {
13
+ const [searchParams] = useSearchParams();
14
+ const theme = searchParams.get('theme') ?? getCustomThemes()?.[0]?.key;
15
+ const data = useMemo(() => (theme ? fetchVariantData(theme, 'buttonVariants') : []), [theme]);
16
+
17
+ const { dummyClickEventHandler } = useToasterService();
11
18
 
12
- export const LinkButtonBasic: FC = () => (
13
- <>
14
- <SampleDescription>
15
- <p>KolLinkButton renders a link that looks like a button. The sample shows the different styling variants.</p>
16
- </SampleDescription>
19
+ const dummyEventHandler = {
20
+ onClick: dummyClickEventHandler,
21
+ };
22
+ return (
23
+ <>
24
+ <SampleDescription>
25
+ <p>KolLinkButton renders a link that looks like a button. The sample shows the different theme exclusive styling variants.</p>
26
+ </SampleDescription>
17
27
 
18
- <div className="flex flex-wrap gap-2">
19
- <KolLinkButton _label="Primary" _variant="primary" {...ARGS}></KolLinkButton>
20
- <KolLinkButton _label="Secondary" _variant="secondary" {...ARGS}></KolLinkButton>
21
- <KolLinkButton _label="Normal" _variant="normal" {...ARGS}></KolLinkButton>
22
- <KolLinkButton _label="Danger" _variant="danger" {...ARGS}></KolLinkButton>
23
- <KolLinkButton _label="Ghost" _variant="ghost" {...ARGS}></KolLinkButton>
24
- <KolLinkButton _label="Access Key" _variant="primary" _accessKey="c" {...ARGS}></KolLinkButton>
25
- <KolLinkButton _label="Short Key" _variant="primary" _shortKey="s" {...ARGS}></KolLinkButton>
26
- </div>
27
- </>
28
- );
28
+ <div className="grid gap-8">
29
+ <section className="grid gap-4">
30
+ <div className="flex flex-wrap gap-4 items-center">
31
+ {!Array.isArray(data) || data.length === 0 ? (
32
+ <p>This theme has no variants for this component.</p>
33
+ ) : (
34
+ data.map((element) => {
35
+ return (
36
+ <KolLinkButton _href="#/back-page" _icons="kolicon-house" _label={`${element}`} _variant={element} key={element} _on={dummyEventHandler} />
37
+ );
38
+ })
39
+ )}
40
+ </div>
41
+ <div className="flex flex-wrap gap-4 items-center">
42
+ {!Array.isArray(data) || data.length === 0 ? (
43
+ <p>This theme has no variants for this component.</p>
44
+ ) : (
45
+ data.map((element) => {
46
+ return (
47
+ <KolLinkButton
48
+ _href="#/back-page"
49
+ _hideLabel
50
+ _icons="kolicon-settings"
51
+ _label={`${element}`}
52
+ _variant={element}
53
+ key={element}
54
+ _on={dummyEventHandler}
55
+ />
56
+ );
57
+ })
58
+ )}
59
+ </div>
60
+ </section>
61
+ </div>
62
+ </>
63
+ );
64
+ };
@@ -1,4 +1,4 @@
1
- import type { ButtonVariantPropType, KoliBriTableCell, KoliBriTableHeaderCell } from '@public-ui/components';
1
+ import type { KoliBriTableCell, KoliBriTableHeaderCell, VariantClassNamePropType } from '@public-ui/components';
2
2
  import { createReactRenderElement, KolButton, KolButtonLink, KolLink, KolLinkButton, KolTableStateless } from '@public-ui/react-v19';
3
3
  import type { ComponentProps, FC } from 'react';
4
4
  import React from 'react';
@@ -16,7 +16,7 @@ function KolButtonWrapper(props: KolButtonProps) {
16
16
  return <KolButton {...props} _on={dummyEventHandler} />;
17
17
  }
18
18
 
19
- const getButtonHeaderCell = (variant: ButtonVariantPropType): KoliBriTableHeaderCell => {
19
+ const getButtonHeaderCell = (variant: VariantClassNamePropType): KoliBriTableHeaderCell => {
20
20
  const variantLabel = `${variant}`;
21
21
  const capitalizedVariant = variantLabel.charAt(0).toUpperCase() + variantLabel.slice(1);
22
22
  return {
@@ -53,8 +53,8 @@ export const TABLE_ROUTES: Routes = {
53
53
  'stateful-export': TableStatefulExport,
54
54
  'stateful-reset-sort': TableStatefulResetSort,
55
55
  'stateful-settings-persistence': TableStatefulSettingsPersistence,
56
- 'stateful-with-selection': TableStatefulWithSelection,
57
56
  'stateless-with-settings-menu': TableStatelessWithSettingsMenu,
57
+ 'stateful-with-selection': TableStatefulWithSelection,
58
58
  'stateful-with-single-selection': TableStatefulWithSingleSelection,
59
59
  stateless: TableStateless,
60
60
  'stateless-async': TableStatelessAsync,
@@ -1,111 +1,101 @@
1
- import type { KoliBriTableCell, KoliBriTableDataType, KoliBriTableSelection } from '@public-ui/components';
2
- import { createReactRenderElement, KolButton, KolTableStateful } from '@public-ui/react-v19';
1
+ import type { KoliBriTableDataType, KoliBriTableSelection } from '@public-ui/components';
2
+ import { KolTableStateful } from '@public-ui/react-v19';
3
3
  import type { FC } from 'react';
4
- import React, { useEffect, useRef, useState } from 'react';
5
- import { useToasterService } from '../../hooks/useToasterService';
6
- import { getRoot } from '../../shares/react-roots';
4
+ import React from 'react';
5
+
7
6
  import { SampleDescription } from '../SampleDescription';
8
7
 
9
- const DATA = [
10
- { id: '1001', name: 'Foo Bar', internalIdentifier: `AAA1001` },
11
- { id: '1002', name: 'Foo Baz', internalIdentifier: `AAA1002` },
12
- { id: '1003', name: 'This row is always unchecked', internalIdentifier: `AAA1003` },
13
- { id: '1004', name: 'This row is always checked', internalIdentifier: `AAA1004` },
14
- ];
15
- type Data = (typeof DATA)[0];
16
- type KolTableStatefulElement = {
17
- addEventListener: (type: string, listener: (event: CustomEvent<Data[]>) => void) => void;
18
- removeEventListener: (type: string, listener: (event: CustomEvent<Data[]>) => void) => void;
19
- getSelection?: () => Promise<KoliBriTableDataType[] | null>;
20
- };
8
+ import type { KoliBriTableHeaderCellWithLogic } from '@public-ui/components';
21
9
 
22
- function KolButtonWrapper({ label }: { label: string }) {
23
- const { dummyClickEventHandler } = useToasterService();
10
+ type ProjectTask = {
11
+ id: string;
12
+ project: string;
13
+ owner: string;
14
+ };
24
15
 
25
- const dummyEventHandler = {
26
- onClick: dummyClickEventHandler,
27
- };
16
+ const HEADERS: { horizontal: KoliBriTableHeaderCellWithLogic[][] } = {
17
+ horizontal: [
18
+ [
19
+ { key: 'id', label: 'ID', width: 80 },
20
+ { key: 'project', label: 'Project', width: 200 },
21
+ { key: 'owner', label: 'Owner', width: 200 },
22
+ {
23
+ type: 'action',
24
+ key: 'actions',
25
+ label: 'Actions',
26
+ width: 250,
27
+ actions: (row) => {
28
+ const simpleRow = row as ProjectTask;
29
+ return [
30
+ {
31
+ type: 'button',
32
+ _label: 'Details',
33
+ _icons: 'kolicon-eye',
34
+ _hideLabel: true,
35
+ _on: {
36
+ onClick: () => alert(`Details: ${simpleRow.id} - ${simpleRow.project}`),
37
+ },
38
+ },
39
+ ];
40
+ },
41
+ },
42
+ ],
43
+ ],
44
+ };
28
45
 
29
- return <KolButton _label={label} _on={dummyEventHandler} />;
30
- }
46
+ const DATA: ProjectTask[] = [
47
+ {
48
+ id: 'T-01',
49
+ project: 'Onboarding checklist',
50
+ owner: 'Alex Rivera',
51
+ },
52
+ {
53
+ id: 'T-02',
54
+ project: 'Accessibility audit',
55
+ owner: 'Jamie Chen',
56
+ },
57
+ {
58
+ id: 'T-03',
59
+ project: 'UX audit',
60
+ owner: 'Tyler Gray',
61
+ },
62
+ {
63
+ id: 'T-04',
64
+ project: 'Software Architectur',
65
+ owner: 'Tess Richardson',
66
+ },
67
+ ];
31
68
 
32
69
  export const TableStatefulWithSelection: FC = () => {
33
- const [selectedValue, setSelectedValue] = useState<Data[] | null>();
34
-
35
70
  const selection: KoliBriTableSelection = {
36
- label: (row) => `Selection for ${(row as Data).name}`,
37
- selectedKeys: selectedValue ? selectedValue.map((element) => element.internalIdentifier) : ['AAA1004'],
38
- disabledKeys: ['AAA1003', 'AAA1004'],
39
- keyPropertyName: 'internalIdentifier',
71
+ label: (row) => `Selection for ${(row as ProjectTask).id}`,
72
+ selectedKeys: ['T-01'],
73
+ disabledKeys: ['T-04'],
74
+ keyPropertyName: 'id',
40
75
  };
41
76
 
42
- const kolTableStatefulRef = useRef<HTMLKolTableStatefulElement>(null);
43
-
44
- const handleSelectionChangeEvent = ({ detail: selection }: CustomEvent<Data[]>) => {
45
- console.log('Selection change via event', selection);
46
- };
47
77
  const handleSelectionChangeCallback = (_event: Event, selection: KoliBriTableDataType[] | null) => {
48
78
  console.log('Selection change via callback', selection);
49
79
  };
50
80
 
51
- const handleButtonClick = async () => {
52
- const tableElement = kolTableStatefulRef.current as unknown as KolTableStatefulElement | null;
53
- const selection = await tableElement?.getSelection?.();
54
- setSelectedValue(selection as Data[] | null);
55
- };
56
-
57
- useEffect(() => {
58
- const tableElement = kolTableStatefulRef.current as unknown as KolTableStatefulElement | null;
59
- const selectionChangeEvent = 'kolSelectionChange';
60
- tableElement?.addEventListener(selectionChangeEvent, handleSelectionChangeEvent);
61
-
62
- return () => {
63
- tableElement?.removeEventListener(selectionChangeEvent, handleSelectionChangeEvent);
64
- };
65
- }, [kolTableStatefulRef]);
66
-
67
- const renderButton = (element: HTMLElement, cell: KoliBriTableCell) => {
68
- const data = (cell as { data?: Data }).data;
69
- const id = data?.id;
70
- getRoot(createReactRenderElement(element)).render(<KolButtonWrapper label={`Click ${id}`} />);
71
- };
72
-
73
81
  return (
74
82
  <>
75
83
  <SampleDescription>
76
- <p>This sample shows KolTableStateful with checkboxes for selection enabled.</p>
84
+ <p>
85
+ Simple example using the refactored action column: Actions are defined once in the column header definition using a factory function. Two rows with
86
+ inline action buttons demonstrate clean separation between data and UI behavior.
87
+ </p>
77
88
  </SampleDescription>
78
89
 
79
90
  <section className="w-full">
80
91
  <KolTableStateful
81
- _label="Table with selection checkboxes"
82
- _headers={{
83
- horizontal: [
84
- [
85
- { key: 'id', label: '#ID', textAlign: 'left' },
86
- { key: 'name', label: 'Name', textAlign: 'left' },
87
- { key: 'action', label: 'Action', textAlign: 'left', render: renderButton },
88
- ],
89
- ],
90
- }}
92
+ _label="Tasks with action buttons"
93
+ _headers={HEADERS}
91
94
  _data={DATA}
95
+ className="block"
92
96
  _selection={selection}
93
97
  _on={{ onSelectionChange: handleSelectionChangeCallback }}
94
- className="block"
95
- style={{ maxWidth: '600px' }}
96
- ref={kolTableStatefulRef}
97
98
  />
98
- <div className="grid grid-cols-3 items-end gap-4 mt-4">
99
- <KolButton
100
- _label="getSelection()"
101
- _on={{
102
- onClick: () => {
103
- void handleButtonClick();
104
- },
105
- }}
106
- ></KolButton>
107
- <pre className="text-base">{JSON.stringify(selectedValue, null, 2)}</pre>
108
- </div>
109
99
  </section>
110
100
  </>
111
101
  );
@@ -1,110 +1,102 @@
1
- import type { KoliBriTableCell, KoliBriTableDataType, KoliBriTableSelection } from '@public-ui/components';
2
- import { createReactRenderElement, KolButton, KolTableStateful } from '@public-ui/react-v19';
1
+ import type { KoliBriTableDataType, KoliBriTableSelection } from '@public-ui/components';
2
+ import { KolTableStateful } from '@public-ui/react-v19';
3
3
  import type { FC } from 'react';
4
- import React, { useEffect, useRef, useState } from 'react';
5
- import { useToasterService } from '../../hooks/useToasterService';
6
- import { getRoot } from '../../shares/react-roots';
4
+ import React from 'react';
5
+
7
6
  import { SampleDescription } from '../SampleDescription';
8
7
 
9
- const DATA = [
10
- { id: '1001', name: 'Foo Bar', internalIdentifier: `AAA1001` },
11
- { id: '1002', name: 'Foo Baz', internalIdentifier: `AAA1002` },
12
- ];
8
+ import type { KoliBriTableHeaderCellWithLogic } from '@public-ui/components';
13
9
 
14
- type Data = (typeof DATA)[0];
15
- type KolTableStatefulElement = {
16
- addEventListener: (type: string, listener: (event: CustomEvent<Data[]>) => void) => void;
17
- removeEventListener: (type: string, listener: (event: CustomEvent<Data[]>) => void) => void;
18
- getSelection?: () => Promise<KoliBriTableDataType[] | null>;
10
+ type ProjectTask = {
11
+ id: string;
12
+ project: string;
13
+ owner: string;
19
14
  };
20
15
 
21
- function KolButtonWrapper({ label }: { label: string }) {
22
- const { dummyClickEventHandler } = useToasterService();
23
-
24
- const dummyEventHandler = {
25
- onClick: dummyClickEventHandler,
26
- };
16
+ const HEADERS: { horizontal: KoliBriTableHeaderCellWithLogic[][] } = {
17
+ horizontal: [
18
+ [
19
+ { key: 'id', label: 'ID', width: 80 },
20
+ { key: 'project', label: 'Project', width: 200 },
21
+ { key: 'owner', label: 'Owner', width: 200 },
22
+ {
23
+ type: 'action',
24
+ key: 'actions',
25
+ label: 'Actions',
26
+ width: 250,
27
+ actions: (row) => {
28
+ const simpleRow = row as ProjectTask;
29
+ return [
30
+ {
31
+ type: 'button',
32
+ _label: 'Start',
33
+ _icons: 'kolicon-chevron-right',
34
+ _variant: 'secondary',
35
+ _on: {
36
+ onClick: () => alert(`Start task ${simpleRow.id}`),
37
+ },
38
+ },
39
+ ];
40
+ },
41
+ },
42
+ ],
43
+ ],
44
+ };
27
45
 
28
- return <KolButton _label={label} _on={dummyEventHandler} />;
29
- }
46
+ const DATA: ProjectTask[] = [
47
+ {
48
+ id: 'T-01',
49
+ project: 'Onboarding checklist',
50
+ owner: 'Alex Rivera',
51
+ },
52
+ {
53
+ id: 'T-02',
54
+ project: 'Accessibility audit',
55
+ owner: 'Jamie Chen',
56
+ },
57
+ {
58
+ id: 'T-03',
59
+ project: 'UX audit',
60
+ owner: 'Tyler Gray',
61
+ },
62
+ {
63
+ id: 'T-04',
64
+ project: 'Software Architectur',
65
+ owner: 'Tess Richardson',
66
+ },
67
+ ];
30
68
 
31
69
  export const TableStatefulWithSingleSelection: FC = () => {
32
- const [selectedValue, setSelectedValue] = useState<Data | null>();
33
-
34
70
  const selection: KoliBriTableSelection = {
35
- label: (row) => `Selection for ${(row as Data).name}`,
71
+ label: (row) => `Selection for ${(row as ProjectTask).id}`,
72
+ selectedKeys: ['T-01'],
73
+ disabledKeys: ['T-04'],
74
+ keyPropertyName: 'id',
36
75
  multiple: false,
37
- selectedKeys: selectedValue ? [selectedValue.internalIdentifier] : [],
38
- keyPropertyName: 'internalIdentifier',
39
76
  };
40
77
 
41
- const kolTableStatefulRef = useRef<HTMLKolTableStatefulElement>(null);
42
-
43
- const handleSelectionChangeEvent = ({ detail: selection }: CustomEvent<Data[]>) => {
44
- console.log('Selection change via event', selection);
45
- };
46
78
  const handleSelectionChangeCallback = (_event: Event, selection: KoliBriTableDataType[] | null) => {
47
79
  console.log('Selection change via callback', selection);
48
80
  };
49
81
 
50
- const handleButtonClick = async () => {
51
- const tableElement = kolTableStatefulRef.current as unknown as KolTableStatefulElement | null;
52
- const selection = await tableElement?.getSelection?.();
53
- setSelectedValue(selection as Data | null);
54
- };
55
-
56
- useEffect(() => {
57
- const tableElement = kolTableStatefulRef.current as unknown as KolTableStatefulElement | null;
58
- const selectionChangeEvent = 'kolSelectionChange';
59
- tableElement?.addEventListener(selectionChangeEvent, handleSelectionChangeEvent);
60
-
61
- return () => {
62
- tableElement?.removeEventListener(selectionChangeEvent, handleSelectionChangeEvent);
63
- };
64
- }, [kolTableStatefulRef]);
65
-
66
- const renderButton = (element: HTMLElement, cell: KoliBriTableCell) => {
67
- const data = (cell as { data?: Data }).data;
68
- const id = data?.id;
69
- getRoot(createReactRenderElement(element)).render(<KolButtonWrapper label={`Click ${id}`} />);
70
- };
71
-
72
82
  return (
73
83
  <>
74
84
  <SampleDescription>
75
- <p>This sample shows KolTableStateful with radio buttons for selection enabled.</p>
85
+ <p>
86
+ Simple example using the refactored action column: Actions are defined once in the column header definition using a factory function. Two rows with
87
+ inline action buttons demonstrate clean separation between data and UI behavior.
88
+ </p>
76
89
  </SampleDescription>
77
90
 
78
91
  <section className="w-full">
79
92
  <KolTableStateful
80
- _label="Table with selection radio"
81
- _headers={{
82
- horizontal: [
83
- [
84
- { key: 'id', label: '#ID', textAlign: 'left' },
85
- { key: 'name', label: 'Name', textAlign: 'left' },
86
- { key: 'action', label: 'Action', textAlign: 'left', render: renderButton },
87
- ],
88
- ],
89
- }}
93
+ _label="Tasks with action buttons"
94
+ _headers={HEADERS}
90
95
  _data={DATA}
96
+ className="block"
91
97
  _selection={selection}
92
98
  _on={{ onSelectionChange: handleSelectionChangeCallback }}
93
- className="block"
94
- style={{ maxWidth: '600px' }}
95
- ref={kolTableStatefulRef}
96
99
  />
97
- <div className="grid grid-cols-3 items-end gap-4 mt-4">
98
- <KolButton
99
- _label="getSelection()"
100
- _on={{
101
- onClick: () => {
102
- void handleButtonClick();
103
- },
104
- }}
105
- ></KolButton>
106
- <pre className="text-base">{JSON.stringify(selectedValue, null, 2)}</pre>
107
- </div>
108
100
  </section>
109
101
  </>
110
102
  );
@@ -34,28 +34,28 @@ export const SameHeightOfAllInteractiveElements: FC = () => {
34
34
  </p>
35
35
  </SampleDescription>
36
36
  <div className="w-full flex flex-wrap gap-4">
37
- <KolButton _icons="fa-solid fa-house" _label="Button" _on={dummyEventHandler} />
38
- <KolButton _hideLabel _icons="fa-solid fa-house" _label="Button" _on={dummyEventHandler} />
39
- <KolLinkButton _href="#" _icons="fa-solid fa-house" _label="Link-Button" _on={dummyEventHandler} />
40
- <KolLinkButton _hideLabel _href="#" _icons="fa-solid fa-house" _label="Link-Button" _on={dummyEventHandler} />
41
- <KolCombobox _hideLabel _icons="fa-solid fa-house" _label="Combobox" _suggestions={[]} />
37
+ <KolButton _icons="kolicon-house" _label="Button" _on={dummyEventHandler} />
38
+ <KolButton _hideLabel _icons="kolicon-house" _label="Button" _on={dummyEventHandler} />
39
+ <KolLinkButton _href="#" _icons="kolicon-house" _label="Link-Button" _on={dummyEventHandler} />
40
+ <KolLinkButton _hideLabel _href="#" _icons="kolicon-house" _label="Link-Button" _on={dummyEventHandler} />
41
+ <KolCombobox _hideLabel _icons="kolicon-house" _label="Combobox" _suggestions={[]} />
42
42
  <KolInputColor
43
43
  _hideLabel
44
44
  _icons={{
45
- left: 'fa-solid fa-house',
45
+ left: 'kolicon-house',
46
46
  }}
47
47
  _label="Input-Color"
48
48
  />
49
- <KolInputFile _hideLabel _icons="fa-solid fa-house" _label="Input-File" />
50
- <KolInputDate _hideLabel _icons="fa-solid fa-house" _label="Input-Date" />
51
- <KolInputEmail _hideLabel _icons="fa-solid fa-house" _label="Input-Email" />
52
- <KolInputNumber _hideLabel _icons="fa-solid fa-house" _label="Input-Number" />
53
- <KolInputPassword _hideLabel _icons="fa-solid fa-house" _label="Input-Password" />
54
- <KolInputRange _hideLabel _icons="fa-solid fa-house" _label="Input-Range" />
55
- <KolInputText _hideLabel _icons="fa-solid fa-house" _label="Input-Text" />
56
- <KolSelect _hideLabel _icons="fa-solid fa-house" _label="Combobox" _options={[]} />
57
- <KolSingleSelect _hideLabel _icons="fa-solid fa-house" _label="Combobox" _options={[]} />
58
- <KolTextarea _hideLabel _icons="fa-solid fa-house" _label="Combobox" />
49
+ <KolInputFile _hideLabel _icons="kolicon-house" _label="Input-File" />
50
+ <KolInputDate _hideLabel _icons="kolicon-house" _label="Input-Date" />
51
+ <KolInputEmail _hideLabel _icons="kolicon-house" _label="Input-Email" />
52
+ <KolInputNumber _hideLabel _icons="kolicon-house" _label="Input-Number" />
53
+ <KolInputPassword _hideLabel _icons="kolicon-house" _label="Input-Password" />
54
+ <KolInputRange _hideLabel _icons="kolicon-house" _label="Input-Range" />
55
+ <KolInputText _hideLabel _icons="kolicon-house" _label="Input-Text" />
56
+ <KolSelect _hideLabel _icons="kolicon-house" _label="Select" _options={[]} />
57
+ <KolSingleSelect _hideLabel _icons="kolicon-house" _label="Single Select" _options={[]} />
58
+ <KolTextarea _hideLabel _icons="kolicon-house" _label="Textarea" />
59
59
  </div>
60
60
  </>
61
61
  );