@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.
- package/assets/img_avatar.jpg +0 -0
- package/assets/sample-image.png +0 -0
- package/package.json +5 -5
- package/src/components/abbr/basic.tsx +2 -6
- package/src/components/button/buttons-in-fixed-grid.tsx +27 -0
- package/src/components/button/routes.ts +2 -0
- package/src/components/button/variants.tsx +30 -10
- package/src/components/dialog/routes.ts +2 -0
- package/src/components/dialog/scroll-lock.tsx +46 -0
- package/src/components/handout/basic.tsx +2 -6
- package/src/components/link-button/basic.tsx +56 -20
- package/src/components/table/interactive-child-elements.tsx +2 -2
- package/src/components/table/routes.ts +1 -1
- package/src/components/table/stateful-with-selection.tsx +72 -82
- package/src/components/table/stateful-with-single-selection.tsx +71 -79
- package/src/scenarios/same-height-of-all-interactive-elements.tsx +16 -16
|
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
|
+
"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.
|
|
31
|
-
"@public-ui/react-
|
|
32
|
-
"@public-ui/react-
|
|
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
|
-
"
|
|
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
|
|
10
|
+
<p>KolAbbr shows an abbreviation.</p>
|
|
11
11
|
</SampleDescription>
|
|
12
12
|
|
|
13
13
|
<p>
|
|
14
|
-
I am <KolAbbr
|
|
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>
|
|
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
|
|
23
|
-
<div className="flex flex-wrap gap-4">
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
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
|
|
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
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 {
|
|
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:
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
5
|
-
|
|
6
|
-
import { getRoot } from '../../shares/react-roots';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
7
6
|
import { SampleDescription } from '../SampleDescription';
|
|
8
7
|
|
|
9
|
-
|
|
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
|
-
|
|
23
|
-
|
|
10
|
+
type ProjectTask = {
|
|
11
|
+
id: string;
|
|
12
|
+
project: string;
|
|
13
|
+
owner: string;
|
|
14
|
+
};
|
|
24
15
|
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
|
37
|
-
selectedKeys:
|
|
38
|
-
disabledKeys: ['
|
|
39
|
-
keyPropertyName: '
|
|
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>
|
|
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="
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
5
|
-
|
|
6
|
-
import { getRoot } from '../../shares/react-roots';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
7
6
|
import { SampleDescription } from '../SampleDescription';
|
|
8
7
|
|
|
9
|
-
|
|
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
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
getSelection?: () => Promise<KoliBriTableDataType[] | null>;
|
|
10
|
+
type ProjectTask = {
|
|
11
|
+
id: string;
|
|
12
|
+
project: string;
|
|
13
|
+
owner: string;
|
|
19
14
|
};
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
|
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>
|
|
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="
|
|
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="
|
|
38
|
-
<KolButton _hideLabel _icons="
|
|
39
|
-
<KolLinkButton _href="#" _icons="
|
|
40
|
-
<KolLinkButton _hideLabel _href="#" _icons="
|
|
41
|
-
<KolCombobox _hideLabel _icons="
|
|
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: '
|
|
45
|
+
left: 'kolicon-house',
|
|
46
46
|
}}
|
|
47
47
|
_label="Input-Color"
|
|
48
48
|
/>
|
|
49
|
-
<KolInputFile _hideLabel _icons="
|
|
50
|
-
<KolInputDate _hideLabel _icons="
|
|
51
|
-
<KolInputEmail _hideLabel _icons="
|
|
52
|
-
<KolInputNumber _hideLabel _icons="
|
|
53
|
-
<KolInputPassword _hideLabel _icons="
|
|
54
|
-
<KolInputRange _hideLabel _icons="
|
|
55
|
-
<KolInputText _hideLabel _icons="
|
|
56
|
-
<KolSelect _hideLabel _icons="
|
|
57
|
-
<KolSingleSelect _hideLabel _icons="
|
|
58
|
-
<KolTextarea _hideLabel _icons="
|
|
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
|
);
|