Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions playground/entries/Tag.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import EditM from '@semcore/icon/Edit/m';
import type { TagProps } from '@semcore/ui/tag';
import type { NSTag } from '@semcore/ui/tag';
import { TagContainer } from '@semcore/ui/tag';
import React from 'react';

Expand All @@ -13,7 +13,7 @@ type AdditionalJSXProps = {
addon: (typeof Addons)[number];
closeIcon: boolean;
};
export type TagJSXProps = JSXProps<TagProps> & AdditionalJSXProps;
export type TagJSXProps = JSXProps<NSTag.Props> & AdditionalJSXProps;

function getJSX(props: TagJSXProps) {
return (
Expand Down
8 changes: 4 additions & 4 deletions semcore/input-tags/src/InputTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import fire from '@semcore/core/lib/utils/fire';
import { getAccessibleName } from '@semcore/core/lib/utils/getAccessibleName';
import uniqueIDEnhancement from '@semcore/core/lib/utils/uniqueID';
import Input, { type InputProps, type InputValueProps } from '@semcore/input';
import Tag, { type TagProps, TagContainer, type TagTextProps, type TagContext } from '@semcore/tag';
import Tag, { type NSTag, TagContainer } from '@semcore/tag';
import React from 'react';

import style from './style/input-tag.shadow.css';
Expand Down Expand Up @@ -44,7 +44,7 @@ export type InputTagsProps = Omit<InputProps, 'size'> &
locale?: string;
};

export type InputTagsTagProps = TagProps & {
export type InputTagsTagProps = NSTag.Props & {
/** Property enabling the ability to remove a tag on click */
editable?: boolean;
};
Expand Down Expand Up @@ -399,8 +399,8 @@ const InputTags = createComponent(InputTagsRoot, {
Value: typeof Input.Value;
TagsContainer: Intergalactic.Component<'ul'>;
Tag: Intergalactic.Component<'div', InputTagsTagProps> & {
Text: Intergalactic.Component<'div', TagProps, TagContext> & {
Content: Intergalactic.Component<'div', TagTextProps>;
Text: Intergalactic.Component<'div', NSTag.Props, NSTag.Ctx> & {
Content: Intergalactic.Component<'div', NSTag.Text.Props>;
};
Close: typeof TagContainer.Close;
Addon: typeof Tag.Addon;
Expand Down
2 changes: 1 addition & 1 deletion semcore/tab-panel/src/TabPanel.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ declare namespace NSTabPanel {
/** @deprecated It will be removed in v18. */
export type TabPanelValue = NSTabPanel.Value;
/** @deprecated It will be removed in v18. */
export type TabPanelProps<T extends TabPanelValue = TabPanelValue> = NSTabPanel.Props<T>;
export type TabPanelProps<T extends NSTabPanel.Value = NSTabPanel.Value> = NSTabPanel.Props<T>;
/** @deprecated It will be removed in v18. */
export type TabPanelItemProps = NSTabPanel.Item.Props;
/** @deprecated It will be removed in v18. */
Expand Down
2 changes: 1 addition & 1 deletion semcore/tag/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"author": "UI-kit team <ui-kit-team@semrush.com>",
"license": "MIT",
"scripts": {
"build": "pnpm semcore-builder --source=js && pnpm vite build"
"build": "pnpm semcore-builder && pnpm vite build"
},
"exports": {
"types": "./lib/types/index.d.ts",
Expand Down
106 changes: 64 additions & 42 deletions semcore/tag/src/Tag.jsx → semcore/tag/src/Tag.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Box } from '@semcore/base-components';
import type { Intergalactic } from '@semcore/core';
import { createComponent, Component, sstyled, Root } from '@semcore/core';
import addonTextChildren from '@semcore/core/lib/utils/addonTextChildren';
import i18nEnhance from '@semcore/core/lib/utils/enhances/i18nEnhance';
Expand All @@ -12,12 +13,16 @@ import { Text as TypographyText } from '@semcore/typography';
import React from 'react';

import style from './style/tag.shadow.css';
import type { NSTag } from './Tag.type';
import { localizedMessages } from './translations/__intergalactic-dynamic-locales';

class RootTag extends Component {
class RootTag extends Component<
Intergalactic.InternalTypings.InferComponentProps<NSTag.Component>,
typeof RootTag.enhance
> {
static displayName = 'Tag';
static style = style;
static enhance = [i18nEnhance(localizedMessages), uniqueIDEnhancement(), resolveColorEnhance()];
static enhance = [i18nEnhance(localizedMessages), uniqueIDEnhancement(), resolveColorEnhance()] as const;
static defaultProps = {
theme: 'primary',
color: 'gray-500',
Expand All @@ -44,12 +49,15 @@ class RootTag extends Component {
};
}

handleKeyDown = (event) => {
handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
switch (event.key) {
case ' ':
case 'Enter':
if (this.asProps.onClick) {
event.preventDefault();

// TODO: Keyboard event isn't assignable to MouseEvent.
// @ts-ignore
this.asProps.onClick(event);
}
break;
Expand All @@ -75,47 +83,50 @@ class RootTag extends Component {
const isInteractive = !disabled && interactive;

return sstyled(styles)(
<>
<STag
render={Box}
id={id}
use:interactive={isInteractive}
use:interactiveView={isInteractiveView}
tag-color={resolveColor(color)}
onKeyDown={this.handleKeyDown}
use:tabIndex={isInteractive ? 0 : -1}
role={isInteractive ? 'button' : undefined}
ref={this.tagRef}
>
{addonLeft ? <Tag.Addon tag={addonLeft} /> : null}
{addonTextChildren(Children, Tag.Text, [Tag.Addon, TagContainer.Circle])}
{addonRight ? <Tag.Addon tag={addonRight} /> : null}
</STag>
</>,
<STag
render={Box}
id={id}
use:interactive={isInteractive}
use:interactiveView={isInteractiveView}
tag-color={resolveColor(color)}
onKeyDown={this.handleKeyDown}
use:tabIndex={isInteractive ? 0 : -1}
role={isInteractive ? 'button' : undefined}
ref={this.tagRef}
>
{addonLeft ? <Tag.Addon tag={addonLeft} /> : null}
{addonTextChildren(Children, Tag.Text, [Tag.Addon, TagContainer.Circle])}
{addonRight ? <Tag.Addon tag={addonRight} /> : null}
</STag>,
);
}
}

class RootTagContainer extends Component {
class RootTagContainer extends Component<
Intergalactic.InternalTypings.InferComponentProps<NSTag.Container.Component>,
typeof RootTagContainer.enhance
> {
static displayName = 'TagContainer';
static style = style;
static enhance = [i18nEnhance(localizedMessages), uniqueIDEnhancement(), resolveColorEnhance()];
static enhance = [i18nEnhance(localizedMessages), uniqueIDEnhancement(), resolveColorEnhance()] as const;
static defaultProps = {
color: 'gray-500',
theme: 'primary',
};

tagRef = React.createRef();
tagRef = React.createRef<HTMLDivElement>();

componentWillUnmount() {
const tagElement = this.tagRef.current;

if (isFocusInside(tagElement)) {
if (tagElement && isFocusInside(tagElement)) {
const nextTagElement = tagElement.nextElementSibling;
if (nextTagElement) {
const nextParentElementSibling = tagElement.parentElement?.nextElementSibling;

if (nextTagElement && nextTagElement instanceof HTMLElement) {
setFocus(nextTagElement);
} else {
setFocus(tagElement.parentElement?.nextElementSibling);
} else if (nextParentElementSibling && nextParentElementSibling instanceof HTMLElement) {
setFocus(nextParentElementSibling);
}
}
}
Expand Down Expand Up @@ -222,7 +233,10 @@ class RootTagContainer extends Component {
}
}

class RootCloseTagContainer extends Component {
class Close extends Component<
Intergalactic.InternalTypings.InferComponentProps<NSTag.Close.Component>,
typeof Close.enhance
> {
static displayName = 'CloseTagContainer';
static style = style;

Expand All @@ -233,13 +247,13 @@ class RootCloseTagContainer extends Component {
size: 'm',
i18n: localizedMessages,
locale: 'en',
children: <CloseTagContainer.Close />,
children: <CloseM />,
};
};

static enhance = [resolveColorEnhance()];
static enhance = [resolveColorEnhance()] as const;

handleKeyDown = (event) => {
handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
const { onKeyDown, onClick } = this.asProps;

if (onKeyDown) {
Expand All @@ -248,6 +262,9 @@ class RootCloseTagContainer extends Component {

if (onClick && (event.key === 'Enter' || event.key === ' ')) {
event.preventDefault();

// TODO: Keyboard event isn't assignable to MouseEvent.
// @ts-ignore
onClick(event);
}
};
Expand All @@ -272,7 +289,9 @@ class RootCloseTagContainer extends Component {
}
}

function TagContainerCircle(props) {
function TagContainerCircle(
props: Intergalactic.InternalTypings.InferChildComponentProps<NSTag.Circle.Component, typeof RootTagContainer, 'Circle'>,
) {
const SAddon = Box;
const SCircle = Root;
const { styles, color, resolveColor } = props;
Expand All @@ -283,7 +302,9 @@ function TagContainerCircle(props) {
);
}

function Text(props) {
function Text(
props: Intergalactic.InternalTypings.InferChildComponentProps<NSTag.Text.Component, typeof RootTag, 'Text'>,
) {
const SText = Root;
const { styles, tagRef } = props;

Expand All @@ -297,7 +318,9 @@ function Text(props) {
);
}

function Addon(props) {
function Addon(
props: Intergalactic.InternalTypings.InferChildComponentProps<NSTag.Addon.Component, typeof RootTagContainer, 'Addon'>,
) {
const SAddon = Root;
const { styles, color, resolveColor } = props;

Expand All @@ -309,7 +332,10 @@ function Addon(props) {
return sstyled(styles)(<SAddon render={Box} tag='div' tag-color={tagColor} />);
}

function Circle(props) {
function Circle(
props: Intergalactic.InternalTypings.InferChildComponentProps<NSTag.Circle.Component, typeof RootTag, 'Circle'>
& Intergalactic.InternalTypings.InferChildComponentProps<NSTag.Circle.Component, typeof RootTagContainer, 'Circle'>,
) {
const SCircle = Root;
const { styles, color, resolveColor } = props;
const tagColor = React.useMemo(() => {
Expand All @@ -323,17 +349,13 @@ const Tag = createComponent(RootTag, {
Text,
Addon,
Circle,
});

const CloseTagContainer = createComponent(RootCloseTagContainer, {
Close: CloseM,
});
}) as NSTag.Component;

export const TagContainer = createComponent(RootTagContainer, {
Tag,
Addon,
Close: CloseTagContainer,
Close,
Circle: TagContainerCircle,
});
}) as NSTag.Container.Component;

export default Tag;
104 changes: 104 additions & 0 deletions semcore/tag/src/Tag.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { BoxProps } from '@semcore/base-components';
import type { PropGetterFn, Intergalactic } from '@semcore/core';
import type { IconProps } from '@semcore/icon';
import type { NSText } from '@semcore/typography';
import type React from 'react';

declare namespace NSTag {
type Size = 'xl' | 'l' | 'm';
type Theme = 'primary' | 'secondary' | 'additional';
type Use = 'primary' | 'secondary';
type Ctx = Props & {
getCloseProps?: PropGetterFn;
};
type Props = BoxProps & {
/** Value responsible for tag availability
*/
disabled?: boolean;
/** Value responsible for tag activity
*/
active?: boolean;
/** Interactive tag
*/
interactive?: boolean;
/** Tag theme, there are several default themes or you can use your color
* @default primary
*/
theme?: NSTag.Theme;
/** Tag color text */
color?: string;
/** Tag size
* @default m
*/
size?: NSTag.Size;
/** Left addon tag */
addonLeft?: React.ElementType;
/** Right addon tag */
addonRight?: React.ElementType;
/** Specifies the locale for i18n support */
locale?: string;
};

namespace Text {
type Props = NSText.Props;
type Component = Intergalactic.Component<'div', Props>;
}

namespace Addon {
type Props = BoxProps;
type Component = Intergalactic.Component<'div', Props>;
}

namespace Circle {
type Props = BoxProps;
type Component = Intergalactic.Component<'div', Props>;
}

namespace Close {
type Props = IconProps & {
/** Tag type
* @default secondary
*/
use?: NSTag.Use;
/** Tag theme, there are several default themes or you can use your color
* @default muted
*/
theme?: NSTag.Theme;
};
type Component = Intergalactic.Component<'button', Props>;
}

namespace Container {
type Component = Intergalactic.Component<'div', NSTag.Props, NSTag.Ctx> & {
Tag: NSTag.Component;
Close: NSTag.Close.Component;
Addon: NSTag.Addon.Component;
Circle: NSTag.Circle.Component;
};
}

type Component = Intergalactic.Component<'div', Props, Ctx> & {
Text: Text.Component;
Addon: Addon.Component;
Circle: Circle.Component;
};
}

/** @deprecated It will be removed in v18. */
export type TagSize = NSTag.Size;
/** @deprecated It will be removed in v18. */
export type TagTheme = NSTag.Theme;
/** @deprecated It will be removed in v18. */
export type TagUse = NSTag.Use;
/** @deprecated It will be removed in v18. */
export type TagProps = NSTag.Props;
/** @deprecated It will be removed in v18. */
export type TagCloseProps = NSTag.Close.Props;
/** @deprecated It will be removed in v18. */
export type TagContext = NSTag.Ctx;
/** @deprecated It will be removed in v18. */
export type TagAddonProps = NSTag.Addon.Props;
/** @deprecated It will be removed in v18. */
export type TagTextProps = NSTag.Text.Props;

export type { NSTag };
Loading
Loading