Shared:Components:Tags Rewrite to typescript
This commit is contained in:
parent
a509bbd0d6
commit
2e6ab2d06a
@ -85,6 +85,7 @@ import { TimePicker } from "./time-picker";
|
||||
import { ArticleItem } from "./article-item";
|
||||
import { ToggleContent } from "./toggle-content";
|
||||
import { Tag } from "./tag";
|
||||
import { Tags } from "./tags";
|
||||
|
||||
export type {
|
||||
TFallbackAxisSideDirection,
|
||||
@ -98,6 +99,7 @@ export type {
|
||||
|
||||
export {
|
||||
Tag,
|
||||
Tags,
|
||||
ToggleContent,
|
||||
ArticleItem,
|
||||
TimePicker,
|
||||
|
27
packages/shared/components/tags/Tags.stories.tsx
Normal file
27
packages/shared/components/tags/Tags.stories.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Tags } from "./Tags";
|
||||
|
||||
type TagsType = typeof Tags;
|
||||
type Story = StoryObj<TagsType>;
|
||||
|
||||
const meta: Meta<TagsType> = {
|
||||
title: "Components/Tags",
|
||||
component: Tags,
|
||||
parameters: {
|
||||
design: {
|
||||
type: "figma",
|
||||
url: "https://www.figma.com/file/ZiW5KSwb4t7Tj6Nz5TducC/UI-Kit-DocSpace-1.0.0?type=design&node-id=62-2597&mode=design&t=TBNCKMQKQMxr44IZ-0",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
tags: ["tag1", "tag2"],
|
||||
columnCount: 2,
|
||||
onSelectTag: () => {},
|
||||
},
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledTags = styled.div`
|
||||
width: 100%;
|
163
packages/shared/components/tags/Tags.tsx
Normal file
163
packages/shared/components/tags/Tags.tsx
Normal file
@ -0,0 +1,163 @@
|
||||
import React, { FC } from "react";
|
||||
|
||||
import { Tag } from "../tag";
|
||||
|
||||
import StyledTags from "./Tags.styled";
|
||||
import { isTagType } from "./Tags.utils";
|
||||
import type { TagType, TagsProps } from "./Tags.types";
|
||||
|
||||
export const Tags: FC<TagsProps> = ({
|
||||
id,
|
||||
tags,
|
||||
style,
|
||||
className,
|
||||
columnCount,
|
||||
onSelectTag,
|
||||
}) => {
|
||||
const [renderedTags, setRenderedTags] = React.useState<TagType[]>([]);
|
||||
|
||||
const tagsRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const updateRenderedTags = React.useCallback(() => {
|
||||
if (tags && tagsRef) {
|
||||
if (!columnCount) return;
|
||||
|
||||
const newTags: TagType[] = [];
|
||||
const containerWidth = tagsRef.current?.offsetWidth ?? 0;
|
||||
|
||||
if (tags.length === 1) {
|
||||
const firstTag = tags[0];
|
||||
|
||||
if (isTagType(firstTag) && firstTag?.isDefault) {
|
||||
const tag = { ...firstTag, maxWidth: `100%` };
|
||||
newTags.push(tag);
|
||||
} else if (isTagType(firstTag) && firstTag?.isThirdParty) {
|
||||
const tag = { ...firstTag, maxWidth: `36px` };
|
||||
newTags.push(tag);
|
||||
} else {
|
||||
const label = isTagType(firstTag) ? firstTag.label : firstTag;
|
||||
|
||||
const tag = { label, maxWidth: `100%` };
|
||||
newTags.push(tag);
|
||||
}
|
||||
|
||||
return setRenderedTags(newTags);
|
||||
}
|
||||
|
||||
if (
|
||||
columnCount >= tags.length ||
|
||||
(tags.length === 2 &&
|
||||
isTagType(tags[0]) &&
|
||||
tags[0]?.isThirdParty &&
|
||||
isTagType(tags[1]) &&
|
||||
tags[1]?.isDefault)
|
||||
) {
|
||||
const thirdPartyTagCount =
|
||||
isTagType(tags[0]) && tags[0]?.isThirdParty ? 1 : 0;
|
||||
|
||||
const currentTagMaxWidth =
|
||||
(containerWidth -
|
||||
thirdPartyTagCount * 40 -
|
||||
(tags.length - thirdPartyTagCount) * 4) /
|
||||
(tags.length - thirdPartyTagCount);
|
||||
|
||||
const maxWidthPercent = Math.floor(
|
||||
(currentTagMaxWidth / containerWidth) * 100,
|
||||
);
|
||||
|
||||
for (let i = 0; i < tags.length; i += 1) {
|
||||
const tag = tags[i];
|
||||
const isTag = isTagType(tag);
|
||||
|
||||
if (isTag && tag?.isThirdParty) {
|
||||
const tagNew = { ...tag, maxWidth: `36px` };
|
||||
newTags.push(tagNew);
|
||||
} else if (isTag && tag?.isDefault) {
|
||||
const tagNew = { ...tag, maxWidth: `${maxWidthPercent}%` };
|
||||
newTags.push(tagNew);
|
||||
} else {
|
||||
const tagNew = {
|
||||
label: isTag ? tag.label : tag,
|
||||
maxWidth: `${maxWidthPercent}%`,
|
||||
};
|
||||
newTags.push(tagNew);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const tagWithDropdown = {
|
||||
label: "",
|
||||
key: "selector",
|
||||
advancedOptions: tags.slice(
|
||||
columnCount,
|
||||
tags.length,
|
||||
) as React.ReactNode[],
|
||||
};
|
||||
|
||||
const currentTagMaxWidth =
|
||||
(containerWidth - columnCount * 4 - 35) / columnCount;
|
||||
|
||||
const maxWidthPercent = Math.floor(
|
||||
(currentTagMaxWidth / containerWidth) * 100,
|
||||
);
|
||||
|
||||
if (columnCount !== 0) {
|
||||
for (let i = 0; i < columnCount; i += 1) {
|
||||
const tag = tags[i];
|
||||
const isTag = isTagType(tag);
|
||||
|
||||
if (isTag && tag?.isThirdParty) {
|
||||
const tagNew = { ...tag, maxWidth: `36px` };
|
||||
newTags.push(tagNew);
|
||||
} else if (isTag && tag?.isDefault) {
|
||||
const tagNew = { ...tag, maxWidth: `${maxWidthPercent}%` };
|
||||
newTags.push(tagNew);
|
||||
} else {
|
||||
const tagNew = {
|
||||
label: isTag ? tag.label : tag,
|
||||
maxWidth: `${maxWidthPercent}%`,
|
||||
};
|
||||
newTags.push(tagNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newTags.push(tagWithDropdown);
|
||||
|
||||
newTags[newTags.length - 1].maxWidth = `35px`;
|
||||
}
|
||||
|
||||
setRenderedTags(newTags);
|
||||
}
|
||||
}, [tags, tagsRef, columnCount]);
|
||||
|
||||
React.useEffect(() => {
|
||||
updateRenderedTags();
|
||||
}, [updateRenderedTags]);
|
||||
|
||||
return (
|
||||
<StyledTags
|
||||
data-testid="tags"
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
ref={tagsRef}
|
||||
>
|
||||
{renderedTags?.length > 0 &&
|
||||
renderedTags.map((tag, idx) => {
|
||||
return (
|
||||
<Tag
|
||||
{...tags}
|
||||
key={tag.label}
|
||||
tag={tag.label}
|
||||
advancedOptions={tag.advancedOptions}
|
||||
tagMaxWidth={tag.maxWidth}
|
||||
isNewTag={false}
|
||||
label={tag.label}
|
||||
onClick={onSelectTag}
|
||||
isLast={idx === renderedTags.length - 1}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledTags>
|
||||
);
|
||||
};
|
28
packages/shared/components/tags/Tags.types.ts
Normal file
28
packages/shared/components/tags/Tags.types.ts
Normal file
@ -0,0 +1,28 @@
|
||||
export type TagType = {
|
||||
key?: string;
|
||||
isDefault?: boolean;
|
||||
isThirdParty?: boolean;
|
||||
/** Accepts the tag label */
|
||||
label: string;
|
||||
/** Accepts the max width of the tag */
|
||||
maxWidth?: string;
|
||||
/** Accepts the dropdown options */
|
||||
advancedOptions?: React.ReactNode[];
|
||||
/** Accepts the tag styles as disabled and disables clicking */
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
export interface TagsProps {
|
||||
/** Accepts id */
|
||||
id?: string;
|
||||
/** Accepts the tags */
|
||||
tags: Array<TagType | string>;
|
||||
/** Accepts class */
|
||||
className?: string;
|
||||
/** Accepts the tag column count */
|
||||
columnCount: number;
|
||||
/** Accepts css style */
|
||||
style?: React.CSSProperties;
|
||||
/** Accepts the function that is called when the tag is selected */
|
||||
onSelectTag: (tag?: string) => void;
|
||||
}
|
5
packages/shared/components/tags/Tags.utils.ts
Normal file
5
packages/shared/components/tags/Tags.utils.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import type { TagType } from "./Tags.types";
|
||||
|
||||
export const isTagType = (tag: TagType | string): tag is TagType => {
|
||||
return typeof tag === "object";
|
||||
};
|
1
packages/shared/components/tags/index.ts
Normal file
1
packages/shared/components/tags/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { Tags } from "./Tags";
|
@ -1,151 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Tag from "../tag";
|
||||
|
||||
import StyledTags from "./StyledTags";
|
||||
|
||||
const Tags = ({
|
||||
id,
|
||||
className,
|
||||
style,
|
||||
tags,
|
||||
columnCount,
|
||||
onSelectTag
|
||||
}: any) => {
|
||||
const [renderedTags, setRenderedTags] = React.useState(null);
|
||||
|
||||
const tagsRef = React.useRef(null);
|
||||
|
||||
const updateRenderedTags = React.useCallback(() => {
|
||||
if (tags && tagsRef) {
|
||||
if (!columnCount) return;
|
||||
|
||||
const newTags = [];
|
||||
// @ts-expect-error TS(2531): Object is possibly 'null'.
|
||||
const containerWidth = tagsRef.current.offsetWidth;
|
||||
|
||||
if (tags.length === 1) {
|
||||
if (tags[0]?.isDefault) {
|
||||
const tag = { ...tags[0], maxWidth: `100%` };
|
||||
newTags.push(tag);
|
||||
} else if (tags[0]?.isThirdParty) {
|
||||
const tag = { ...tags[0], maxWidth: `36px` };
|
||||
newTags.push(tag);
|
||||
} else {
|
||||
const tag = { label: tags[0].label || tags[0], maxWidth: `100%` };
|
||||
newTags.push(tag);
|
||||
}
|
||||
|
||||
// @ts-expect-error TS(2345): Argument of type 'any[]' is not assignable to para... Remove this comment to see the full error message
|
||||
return setRenderedTags(newTags);
|
||||
}
|
||||
|
||||
if (
|
||||
columnCount >= tags.length ||
|
||||
(tags.length === 2 && tags[0]?.isThirdParty && tags[1]?.isDefault)
|
||||
) {
|
||||
const thirdPartyTagCount = tags[0]?.isThirdParty ? 1 : 0;
|
||||
|
||||
const currentTagMaxWidth =
|
||||
(containerWidth -
|
||||
thirdPartyTagCount * 40 -
|
||||
(tags.length - thirdPartyTagCount) * 4) /
|
||||
(tags.length - thirdPartyTagCount);
|
||||
|
||||
const maxWidthPercent = Math.floor(
|
||||
(currentTagMaxWidth / containerWidth) * 100
|
||||
);
|
||||
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
if (tags[i]?.isThirdParty) {
|
||||
const tag = { ...tags[i], maxWidth: `36px` };
|
||||
newTags.push(tag);
|
||||
} else if (tags[i]?.isDefault) {
|
||||
const tag = { ...tags[i], maxWidth: `${maxWidthPercent}%` };
|
||||
newTags.push(tag);
|
||||
} else {
|
||||
const tag = { label: tags[i], maxWidth: `${maxWidthPercent}%` };
|
||||
newTags.push(tag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const tagWithDropdown = {
|
||||
key: "selector",
|
||||
advancedOptions: tags.slice(columnCount, tags.length),
|
||||
};
|
||||
|
||||
const currentTagMaxWidth =
|
||||
(containerWidth - columnCount * 4 - 35) / columnCount;
|
||||
|
||||
const maxWidthPercent = Math.floor(
|
||||
(currentTagMaxWidth / containerWidth) * 100
|
||||
);
|
||||
|
||||
if (columnCount !== 0) {
|
||||
for (let i = 0; i < columnCount; i++) {
|
||||
if (tags[i]?.isThirdParty) {
|
||||
const tag = { ...tags[i], maxWidth: `36px` };
|
||||
newTags.push(tag);
|
||||
} else if (tags[i]?.isDefault) {
|
||||
const tag = { ...tags[i], maxWidth: `${maxWidthPercent}%` };
|
||||
newTags.push(tag);
|
||||
} else {
|
||||
const tag = { label: tags[i], maxWidth: `${maxWidthPercent}%` };
|
||||
newTags.push(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newTags.push(tagWithDropdown);
|
||||
|
||||
newTags[newTags.length - 1].maxWidth = `35px`;
|
||||
}
|
||||
|
||||
// @ts-expect-error TS(2345): Argument of type 'any[]' is not assignable to para... Remove this comment to see the full error message
|
||||
setRenderedTags(newTags);
|
||||
}
|
||||
}, [tags, tagsRef, columnCount]);
|
||||
|
||||
React.useEffect(() => {
|
||||
updateRenderedTags();
|
||||
}, [tags, tagsRef, columnCount]);
|
||||
|
||||
return (
|
||||
<StyledTags id={id} className={className} style={style} ref={tagsRef}>
|
||||
// @ts-expect-error TS(2339): Property 'length' does not exist on type 'never'.
|
||||
{renderedTags?.length > 0 &&
|
||||
// @ts-expect-error TS(2531): Object is possibly 'null'.
|
||||
renderedTags.map((tag: any, index: any) => (
|
||||
<Tag
|
||||
key={`${tag.label}_${index}`}
|
||||
label={tag.label}
|
||||
advancedOptions={tag.advancedOptions}
|
||||
tagMaxWidth={tag.maxWidth}
|
||||
tagMinWidth={tag.minWidth}
|
||||
isNewTag={false}
|
||||
onClick={onSelectTag}
|
||||
// @ts-expect-error TS(2531): Object is possibly 'null'.
|
||||
isLast={index === renderedTags.length - 1}
|
||||
{...tag}
|
||||
/>
|
||||
))}
|
||||
</StyledTags>
|
||||
);
|
||||
};
|
||||
|
||||
Tags.propTypes = {
|
||||
/** Accepts the tags */
|
||||
tags: PropTypes.array,
|
||||
/** Accepts the tag column count */
|
||||
columnCount: PropTypes.number,
|
||||
/** Accepts class */
|
||||
className: PropTypes.string,
|
||||
/** Accepts id */
|
||||
id: PropTypes.string,
|
||||
/** Accepts css style */
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
/** Accepts the function that is called when the tag is selected */
|
||||
onSelectTag: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Tags;
|
@ -1,27 +0,0 @@
|
||||
import React from "react";
|
||||
import Tags from ".";
|
||||
|
||||
export default {
|
||||
title: "Components/Tags",
|
||||
component: Tags,
|
||||
parameters: {
|
||||
design: {
|
||||
type: "figma",
|
||||
url: "https://www.figma.com/file/ZiW5KSwb4t7Tj6Nz5TducC/UI-Kit-DocSpace-1.0.0?type=design&node-id=62-2597&mode=design&t=TBNCKMQKQMxr44IZ-0",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args: any) => <Tags {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
// @ts-expect-error TS(2339): Property 'args' does not exist on type '(args: any... Remove this comment to see the full error message
|
||||
Default.args = {
|
||||
tags: ["tag1", "tag2"],
|
||||
id: "",
|
||||
className: "",
|
||||
columnCount: 2,
|
||||
style: {},
|
||||
onSelectTag: (tags: any) => {},
|
||||
};
|
Loading…
Reference in New Issue
Block a user