Skip to content

Commit

Permalink
feat: add DismissableListOutput, DismissableMultListOutput, and Dismi…
Browse files Browse the repository at this point in the history
…ssableTextOutput components
  • Loading branch information
samshara committed Oct 15, 2024
1 parent 4d91b46 commit dd92691
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-tools-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ifrc-go/ui": patch
---

Add DismissableListOutput, DismissableMultListOutput and DismissableTextOutput components
65 changes: 65 additions & 0 deletions packages/ui/src/components/DismissableListOutput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
useCallback,
useMemo,
} from 'react';
import { isNotDefined } from '@togglecorp/fujs';

import Chip from '#components/Chip';

export interface Props<
O,
V extends string | number,
N extends string | number
> {
value: V | undefined;
name: N;
onDismiss: (value: V | undefined, name: N) => void;
keySelector: (value: O) => V;
labelSelector: (value: O) => string;
options: O[] | undefined | null;
}

function DismissableListOutput<
O,
V extends string | number,
N extends string | number
>(
props: Props<O, V, N>,
) {
const {
name,
value,
onDismiss,
labelSelector,
keySelector,
options,
} = props;

const item = useMemo(() => {
if (!Array.isArray(options) || isNotDefined(value)) {
return undefined;
}
return options?.find((option) => keySelector(option) === value);
}, [value, options, keySelector]);

const handleDismiss = useCallback((val: V) => {
onDismiss(val, name);
}, [name, onDismiss]);

if (isNotDefined(value) || isNotDefined(item)) {
return null;
}

const itemName = labelSelector(item);

return (
<Chip
label={itemName}
name={value}
onDelete={handleDismiss}
variant="tertiary"
/>
);
}

export default DismissableListOutput;
72 changes: 72 additions & 0 deletions packages/ui/src/components/DismissableMultiListOutput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
useCallback,
useMemo,
} from 'react';
import { isNotDefined } from '@togglecorp/fujs';

import Chip, { type Props as ChipProps } from '#components/Chip';
import RawList from '#components/RawList';

export interface Props<
O,
V extends string | number,
N extends string | number
> {
value: V[] | undefined;
name: N;
onDismiss: (value: V[] | undefined, name: N) => void;
keySelector: (value: O) => V;
labelSelector: (value: O) => string;
options: O[] | undefined | null;
}

function DismissableMultiListOutput<
O,
V extends string | number,
N extends string | number
>(
props: Props<O, V, N>,
) {
const {
name,
value,
onDismiss,
labelSelector,
keySelector,
options,
} = props;

const values = useMemo(() => {
if (!Array.isArray(options) || !Array.isArray(value)) {
return undefined;
}
return options?.filter((option) => value?.includes(keySelector(option)));
}, [value, options, keySelector]);

const handleDismiss = useCallback((val: V) => {
const updatedValue = value?.filter((item) => item !== val);
onDismiss(updatedValue, name);
}, [name, onDismiss, value]);

const chipRendererParams = useCallback((key: V, item: O): ChipProps<V> => ({
label: labelSelector(item),
name: key,
onDelete: handleDismiss,
variant: 'tertiary',
}), [handleDismiss, labelSelector]);

if (isNotDefined(value) || value.length < 1) {
return null;
}

return (
<RawList
data={values}
renderer={Chip<V>}
rendererParams={chipRendererParams}
keySelector={keySelector}
/>
);
}

export default DismissableMultiListOutput;
39 changes: 39 additions & 0 deletions packages/ui/src/components/DismissableTextOutput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useCallback } from 'react';
import { isNotDefined } from '@togglecorp/fujs';

import Chip from '#components/Chip';

export interface Props<T extends string | number> {
value: string | undefined;
name: T;
onDismiss: (value: undefined, name: T) => void;
}

function DismissableTextOutput<T extends string | number>(
props: Props<T>,
) {
const {
value,
name,
onDismiss,
} = props;

const handleDismiss = useCallback(() => {
onDismiss(undefined, name);
}, [name, onDismiss]);

if (isNotDefined(value)) {
return null;
}

return (
<Chip
label={value}
name={name}
onDelete={handleDismiss}
variant="tertiary"
/>
);
}

export default DismissableTextOutput;
6 changes: 6 additions & 0 deletions packages/ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ export type { Props as DateOutputProps } from './components/DateOutput';
export { default as DateOutput } from './components/DateOutput';
export type { Props as DateRangeOutputProps } from './components/DateRangeOutput';
export { default as DateRangeOutput } from './components/DateRangeOutput';
export type { Props as DismissableListOutputProps } from './components/DismissableListOutput';
export { default as DismissableListOutput } from './components/DismissableListOutput';
export type { Props as DismissableMultiListOutputProps } from './components/DismissableMultiListOutput';
export { default as DismissableMultiListOutput } from './components/DismissableMultiListOutput';
export type { Props as DismissableTextOutputProps } from './components/DismissableTextOutput';
export { default as DismissableTextOutput } from './components/DismissableTextOutput';
export type { Props as DropdownMenuProps } from './components/DropdownMenu';
export { default as DropdownMenu } from './components/DropdownMenu';
export type { Props as ExpandableContainerProps } from './components/ExpandableContainer';
Expand Down

0 comments on commit dd92691

Please sign in to comment.