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/InputTags.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { NSInput } from '@semcore/ui/input';
import InputTags from '@semcore/ui/input-tags';
import type { InputTagsProps } from '@semcore/ui/input-tags';
import type { NSInputTags } from '@semcore/ui/input-tags';
import React from 'react';

import type { JSXProps } from '../types/JSXProps';
Expand All @@ -16,7 +16,7 @@ type AdditionalJSXProps = {
closable: boolean;
};
};
export type InputTagsJSXProps = JSXProps<InputTagsProps & NSInput.Value.Props> & AdditionalJSXProps;
export type InputTagsJSXProps = JSXProps<NSInputTags.Props & NSInput.Value.Props> & AdditionalJSXProps;

function getJSX(props: InputTagsJSXProps) {
return (
Expand Down
106 changes: 31 additions & 75 deletions semcore/input-tags/src/InputTags.tsx
Original file line number Diff line number Diff line change
@@ -1,88 +1,32 @@
import { ScrollArea, Portal, ScreenReaderOnly } from '@semcore/base-components';
import type { NSScrollArea } from '@semcore/base-components';
import {
createComponent,
Component,
sstyled,
Root,
type PropGetterFn,
type Intergalactic,
type IRootComponentProps,
} from '@semcore/core';
import type { WithI18nEnhanceProps } from '@semcore/core/lib/utils/enhances/i18nEnhance';
import i18nEnhance from '@semcore/core/lib/utils/enhances/i18nEnhance';
import { extractFrom, isAdvanceMode } from '@semcore/core/lib/utils/findComponent';
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 NSInput } from '@semcore/input';
import Tag, { type NSTag, TagContainer } from '@semcore/tag';
import Input from '@semcore/input';
import Tag, { TagContainer } from '@semcore/tag';
import React from 'react';

import type { NSInputTags } from './InputTags.type';
import style from './style/input-tag.shadow.css';
import type { LocalizedMessages } from './translations/__intergalactic-dynamic-locales';
import { localizedMessages } from './translations/__intergalactic-dynamic-locales';

export type InputTagsValueProps = NSInput.Value.Props & {};

export type InputTagsSize = 'l' | 'm';

export type InputTagsProps = Omit<NSInput.Props, 'size'> &
NSScrollArea.Props & {
/**
* Component size
* @default m
*/
size?: InputTagsSize;
/** Event is called when tags need to be added */
onAppend?: (values: string[], event: React.KeyboardEvent | React.ClipboardEvent) => void;
/** Event is called when tags need to be removed */
onRemove?: (event: React.KeyboardEvent | React.MouseEvent) => void;
/** List delimiter of tags. Don't forget to add 'Enter' and 'Tab' to hande corresponding hotkeys.
* @default [',', ';', '|', 'Enter', 'Tab']
* */
delimiters?: string[];
/** Specifies the locale for i18n support */
locale?: string;
};

export type InputTagsDefaultProps = {
size: 'm';
delimiters: InputTagsProps['delimiters'];
i18n: LocalizedMessages;
locale: 'en';
};

export type InputTagsTagProps = NSTag.Props & {
/** Property enabling the ability to remove a tag on click */
editable?: boolean;
};

export type InputTagsContext = InputTagsProps & {
getValueProps: PropGetterFn;
getTagProps: PropGetterFn;
};

export type InputTagsComponent = Intergalactic.Component<'div', InputTagsProps, InputTagsContext> & {
Value: typeof Input.Value;
TagsContainer: Intergalactic.Component<'ul'>;
Tag: Intergalactic.Component<'div', InputTagsTagProps> & {
Text: Intergalactic.Component<'div', NSTag.Props, NSTag.Ctx> & {
Content: Intergalactic.Component<'div', NSTag.Text.Props>;
};
Close: typeof TagContainer.Close;
Addon: typeof Tag.Addon;
Circle: typeof Tag.Circle;
};
};

class InputTagsRoot extends Component<
InputTagsProps,
Intergalactic.InternalTypings.InferComponentProps<NSInputTags.Component>,
typeof InputTagsRoot.enhance,
{},
WithI18nEnhanceProps,
{},
InputTagsDefaultProps
NSInputTags.DefaultProps
> {
static displayName = 'InputTags';
static style = style;
Expand Down Expand Up @@ -188,7 +132,7 @@ class InputTagsRoot extends Component<
}
};

onTagClick = (event: React.MouseEvent) => {
onTagClick = (event: React.MouseEvent | React.KeyboardEvent) => {
fire(this, 'onRemove', event);
};

Expand Down Expand Up @@ -278,7 +222,9 @@ class InputTagsRoot extends Component<
}
}

class Value extends Component<InputTagsValueProps> {
class Value extends Component<
Intergalactic.InternalTypings.InferChildComponentProps<NSInputTags.Value.Component, typeof InputTagsRoot, 'Value'>
> {
private _spacer = React.createRef<HTMLDivElement>();

state = {
Expand All @@ -289,7 +235,7 @@ class Value extends Component<InputTagsValueProps> {
this.updateInputStyles(this.asProps.value!);
}

componentDidUpdate(prevProps: any) {
componentDidUpdate(prevProps: typeof this.asProps) {
const { value, placeholder } = this.asProps;
if (value !== prevProps.value || placeholder !== prevProps.placeholder) {
this.updateInputStyles(value!);
Expand Down Expand Up @@ -333,11 +279,14 @@ class Value extends Component<InputTagsValueProps> {
}
}

function InputTagsContainer({
Children,
tagsContainerAriaLabel,
styles,
}: IRootComponentProps & { tagsContainerAriaLabel: string }) {
function InputTagsContainer(
props: Intergalactic.InternalTypings.InferChildComponentProps<
NSInputTags.TagsContainer.Component,
typeof InputTagsRoot,
'TagsContainer'
>,
) {
const { Children, tagsContainerAriaLabel, styles } = props;
const SListAriaWrapper = 'ul';

return sstyled(styles)(
Expand All @@ -347,7 +296,9 @@ function InputTagsContainer({
);
}

function InputTagContainer(props: any) {
function InputTagContainer(
props: Intergalactic.InternalTypings.InferChildComponentProps<NSInputTags.Tag.Component, typeof InputTagsRoot, 'Tag'>,
) {
const STag = Root;

const onKeyDown = React.useCallback(
Expand All @@ -365,15 +316,16 @@ function InputTagContainer(props: any) {

return sstyled(props.styles)(
<STag
data-value={props.value}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this is correct...

render={TagContainer}
tag='li'
onKeyDown={onKeyDown}
interactive={interactive}
/>,
);
}
function InputTagContainerTag(props: any) {
function InputTagContainerTag(
props: Intergalactic.InternalTypings.InferChildComponentProps<NSInputTags.Tag.Text.Component, typeof InputTagsRoot, 'TagText'>,
) {
const STag = Root;
const { getI18nText } = props;

Expand Down Expand Up @@ -402,11 +354,15 @@ function InputTagContainerTag(props: any) {
);
}

function TagContainerTextContent(props: IRootComponentProps) {
function TagContainerTextContent(
props: Intergalactic.InternalTypings.InferComponentProps<NSInputTags.Tag.Text.Content.Component>,
) {
return sstyled(props.styles)(<Root render={Tag.Text} />);
}

function TagCloseButton(props: IRootComponentProps) {
function TagCloseButton(
props: Intergalactic.InternalTypings.InferComponentProps<NSInputTags.Tag.Close.Component>,
) {
const STagContainerClose = Root;
return sstyled(props.styles)(<STagContainerClose render={TagContainer.Close} />);
}
Expand All @@ -417,7 +373,7 @@ function TagCloseButton(props: IRootComponentProps) {
* {@link https://developer.semrush.com/intergalactic/components/input-tags/input-tags-api/|API} | {@link https://developer.semrush.com/intergalactic/components/input-tags/input-tags-code/|Examples}
*/
const InputTags = createComponent<
InputTagsComponent,
NSInputTags.Component,
typeof InputTagsRoot
>(InputTagsRoot, {
Value,
Expand Down
112 changes: 112 additions & 0 deletions semcore/input-tags/src/InputTags.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { NSScrollArea } from '@semcore/base-components';
import type { PropGetterFn, Intergalactic } from '@semcore/core';
import type { NSInput } from '@semcore/input';
import type { NSTag } from '@semcore/tag';

import type { LocalizedMessages } from './translations/__intergalactic-dynamic-locales';

declare namespace NSInputTags {
type Size = 'l' | 'm';
type Props = Omit<NSInput.Props, 'size'> &
NSScrollArea.Props & {
/**
* Component size
* @default m
*/
size?: NSInputTags.Size;
/** Event is called when tags need to be added */
onAppend?: (values: string[], event: React.KeyboardEvent | React.ClipboardEvent) => void;
/** Event is called when tags need to be removed */
onRemove?: (event: React.KeyboardEvent | React.MouseEvent) => void;
/** List delimiter of tags. Don't forget to add 'Enter' and 'Tab' to hande corresponding hotkeys.
* @default [',', ';', '|', 'Enter', 'Tab']
* */
delimiters?: string[];
/** Specifies the locale for i18n support */
locale?: string;
};
type DefaultProps = {
size: 'm';
delimiters: NSInputTags.Props['delimiters'];
i18n: LocalizedMessages;
locale: 'en';
};
type Ctx = {
getValueProps: PropGetterFn;
getTagProps: PropGetterFn;
};

namespace Value {
type Props = NSInput.Value.Props;

type Component = NSInput.Value.Component;
}

namespace TagsContainer {
type Component = Intergalactic.Component<'ul'>;
}

namespace Tag {
type Props = NSTag.Props & {
/** Property enabling the ability to remove a tag on click */
editable?: boolean;
};

namespace Text {
type Props = NSTag.Props;
type Ctx = NSTag.Ctx;

namespace Content {
type Props = NSTag.Text.Props;

type Component = Intergalactic.Component<'div', Props>;
}

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

namespace Close {
type Component = NSTag.Close.Component;
}

namespace Addon {
type Component = NSTag.Addon.Component;
}

namespace Circle {
type Component = NSTag.Circle.Component;
}

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

type Component = Intergalactic.Component<'div', Props, Ctx> & {
Value: Value.Component;
TagsContainer: TagsContainer.Component;
Tag: Tag.Component;
};
}

/** @deprecated It will be removed in v19. */
export type InputTagsSize = NSInputTags.Size;
/** @deprecated It will be removed in v19. */
export type InputTagsProps = NSInputTags.Props;
/** @deprecated It will be removed in v19. */
export type InputTagsDefaultProps = NSInput.DefaultProps;
/** @deprecated It will be removed in v19. */
export type InputTagsContext = NSInputTags.Ctx;
/** @deprecated It will be removed in v19. */
export type InputTagsTagProps = NSInputTags.Tag.Props;
/** @deprecated It will be removed in v19. */
export type InputTagsComponent = NSInputTags.Component;
/** @deprecated It will be removed in v19. */
export type InputTagsValueProps = NSInputTags.Value.Props;

export type { NSInputTags };
2 changes: 1 addition & 1 deletion semcore/input-tags/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default } from './InputTags';
export * from './InputTags';
export * from './InputTags.type';
4 changes: 2 additions & 2 deletions stories/components/ellipsis/docs/examples/input-tags.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Flex } from '@semcore/ui/base-components';
import Ellipsis from '@semcore/ui/ellipsis';
import type { InputTagsProps, InputTagsValueProps, InputTagsTagProps } from '@semcore/ui/input-tags';
import type { NSInputTags } from '@semcore/ui/input-tags';
import InputTags from '@semcore/ui/input-tags';
import { Text } from '@semcore/ui/typography';
import React from 'react';

type ExampleInputTagsProps = InputTagsProps & InputTagsValueProps;
type ExampleInputTagsProps = NSInputTags.Props & NSInputTags.Value.Props;

const Demo = (props: ExampleInputTagsProps) => {
const inputValueRef = React.useRef<HTMLInputElement>(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Flex } from '@semcore/ui/base-components';
import type { InputTagsProps, InputTagsValueProps } from '@semcore/ui/input-tags';
import type { NSInputTags } from '@semcore/ui/input-tags';
import InputTags from '@semcore/ui/input-tags';
import { Text } from '@semcore/ui/typography';
import React from 'react';

type ExampleInputTagsProps = InputTagsProps & InputTagsValueProps;
type ExampleInputTagsProps = NSInputTags.Props & NSInputTags.Value.Props;

const Demo = (props: ExampleInputTagsProps) => {
const inputValueRef = React.useRef<HTMLInputElement>(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Flex } from '@semcore/ui/base-components';
import InputTags from '@semcore/ui/input-tags';
import type { InputTagsTagProps } from '@semcore/ui/input-tags';
import type { NSInputTags } from '@semcore/ui/input-tags';
import { Text } from '@semcore/ui/typography';
import React from 'react';

const isValidEmail = (value: string) => /.+@.+\..+/i.test(value.toLowerCase());

const defaultTags = ['bob@email.com', 'alice@domain.net', 'mary@website.com', 'steve@company.com'];
type ExampleInputTagsProps = InputTagsTagProps;
type ExampleInputTagsProps = NSInputTags.Tag.Props;

const Demo = (props: ExampleInputTagsProps) => {
const [tags, setTags] = React.useState(defaultTags);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Flex } from '@semcore/ui/base-components';
import type { InputTagsProps, InputTagsValueProps, InputTagsTagProps } from '@semcore/ui/input-tags';
import type { NSInputTags } from '@semcore/ui/input-tags';
import InputTags from '@semcore/ui/input-tags';
import { Text } from '@semcore/ui/typography';
import React from 'react';

type ExampleInputTagsProps = InputTagsProps & InputTagsValueProps & InputTagsTagProps & {
type ExampleInputTagsProps = NSInputTags.Props & NSInputTags.Value.Props & NSInputTags.Tag.Props & {
hintPlacement?: 'top' | 'bottom' | 'left' | 'right';
hintProps?: false;
};
Expand Down
Loading
Loading