Browse Source

Attempt to make tag input work

Integrate tag input styling to tag input component.
pull/1/head
TheoryOfNekomata 1 year ago
parent
commit
30e80055bb
6 changed files with 143 additions and 55 deletions
  1. +1
    -0
      packages/web-kitchensink-reactnext/package.json
  2. +14
    -1
      packages/web-kitchensink-reactnext/pnpm-lock.yaml
  3. +77
    -53
      packages/web-kitchensink-reactnext/src/categories/option/react/components/TagInput/index.tsx
  4. +36
    -0
      packages/web-kitchensink-reactnext/src/categories/option/react/components/TagInput/style.module.css
  5. +4
    -1
      packages/web-kitchensink-reactnext/src/pages/categories/option/index.tsx
  6. +11
    -0
      packages/web-kitchensink-reactnext/src/styles/globals.css

+ 1
- 0
packages/web-kitchensink-reactnext/package.json View File

@@ -26,6 +26,7 @@
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-refractor": "^2.1.7", "react-refractor": "^2.1.7",
"react-tag-input-component": "^2.0.2",
"tailwindcss": "3.3.2", "tailwindcss": "3.3.2",
"typescript": "5.1.3", "typescript": "5.1.3",
"wavesurfer.js": "7.0.0-beta.11" "wavesurfer.js": "7.0.0-beta.11"


+ 14
- 1
packages/web-kitchensink-reactnext/pnpm-lock.yaml View File

@@ -1,4 +1,4 @@
lockfileVersion: '6.1'
lockfileVersion: '6.0'


settings: settings:
autoInstallPeers: true autoInstallPeers: true
@@ -56,6 +56,9 @@ dependencies:
react-refractor: react-refractor:
specifier: ^2.1.7 specifier: ^2.1.7
version: 2.1.7(react@18.2.0) version: 2.1.7(react@18.2.0)
react-tag-input-component:
specifier: ^2.0.2
version: 2.0.2(react-dom@18.2.0)(react@18.2.0)
tailwindcss: tailwindcss:
specifier: 3.3.2 specifier: 3.3.2
version: 3.3.2 version: 3.3.2
@@ -2570,6 +2573,16 @@ packages:
unist-util-visit-parents: 3.1.1 unist-util-visit-parents: 3.1.1
dev: false dev: false


/react-tag-input-component@2.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-dydI9luVwwv9vrjE5u1TTnkcOVkOVL6mhFti8r6hLi78V2F2EKWQOLptURz79UYbDHLSk6tnbvGl8FE+sMpADg==}
peerDependencies:
react: ^16 || ^17 || ^18
react-dom: ^16 || ^17 || ^18
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false

/react@18.2.0: /react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}


+ 77
- 53
packages/web-kitchensink-reactnext/src/categories/option/react/components/TagInput/index.tsx View File

@@ -1,7 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import { TagsInput } from 'react-tag-input-component';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import clsx from 'clsx'; import clsx from 'clsx';
import {useEnhanced} from '@/packages/react-utils'; import {useEnhanced} from '@/packages/react-utils';
import styles from './style.module.css';
import {delegateTriggerChangeEvent} from '@/utils/event';


type TagInputDerivedElement = HTMLInputElement; type TagInputDerivedElement = HTMLInputElement;


@@ -43,6 +46,7 @@ export interface TagInputProps extends Omit<React.HTMLProps<TagInputDerivedEleme
*/ */
hiddenLabel?: boolean, hiddenLabel?: boolean,
enhanced?: boolean, enhanced?: boolean,
separator?: string,
} }


/** /**
@@ -64,6 +68,9 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
hiddenLabel = false, hiddenLabel = false,
className, className,
enhanced: enhancedProp = false, enhanced: enhancedProp = false,
separator = ',',
onChange,
defaultValue,
...etcProps ...etcProps
}: TagInputProps, }: TagInputProps,
forwardedRef, forwardedRef,
@@ -72,9 +79,20 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
const defaultRef = React.useRef<TagInputDerivedElement>(null); const defaultRef = React.useRef<TagInputDerivedElement>(null);
const ref = forwardedRef ?? defaultRef; const ref = forwardedRef ?? defaultRef;
const labelId = React.useId(); const labelId = React.useId();
const dummyInputRef = React.useRef<HTMLInputElement>(null);
const tags = React.useMemo(() => {
if (defaultValue === undefined) {
return [];
}
if (typeof defaultValue === 'string') {
return defaultValue.split(separator);
}
if (typeof defaultValue === 'number') {
return [defaultValue.toString()];
}
return defaultValue as string[];
}, [defaultValue, separator]);


const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
const handleTagsInputChange = (tags: string[]) => {
if (!(typeof ref === 'object' && ref)) { if (!(typeof ref === 'object' && ref)) {
return; return;
} }
@@ -82,80 +100,86 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
if (!input) { if (!input) {
return; return;
} }
input.focus();
input.value = tags.join(separator);
setTimeout(() => {
delegateTriggerChangeEvent(input);
});
}; };


const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
if (!(typeof dummyInputRef === 'object' && dummyInputRef)) {
return;
}
const { current: input } = dummyInputRef;
if (!input) {
return;
}
input.value = e.currentTarget.value;
};
const inputClassName = clsx(
'bg-negative rounded-inherit w-full block',
'focus:outline-0',
'disabled:opacity-50 disabled:cursor-not-allowed',
{
'text-xxs': size === 'small',
'text-xs': size === 'medium',
},
{
'pl-4': variant === 'default',
'pl-1.5': variant === 'alternate',
},
{
'pt-4': variant === 'alternate',
},
{
'pr-4': variant === 'default' && !indicator,
'pr-1.5': variant === 'alternate' && !indicator,
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
);


return ( return (
<div <div
className={clsx( className={clsx(
'relative rounded ring-secondary/50',
styles['tag-input'],
size === 'small' && styles['tag-input-small'],
size === 'medium' && styles['tag-input-medium'],
size === 'large' && styles['tag-input-large'],
'relative rounded ring-secondary/50 group',
'focus-within:ring-4', 'focus-within:ring-4',
{ {
'block': block, 'block': block,
'inline-block align-middle': !block, 'inline-block align-middle': !block,
}, },
{
'min-h-10': size === 'small',
'min-h-12': size === 'medium',
'min-h-16': size === 'large',
},
className, className,
)} )}
> >
<input <input
{...etcProps} {...etcProps}
ref={ref} ref={ref}
readOnly={enhanced}
aria-labelledby={labelId} aria-labelledby={labelId}
type={type} type={type}
data-testid="input" data-testid="input"
onChange={onChange}
className={clsx( className={clsx(
'peer',
inputClassName,
!enhanced && {
'h-10': size === 'small',
'h-12': size === 'medium',
'h-16': size === 'large',
},
!enhanced && 'peer',
enhanced && 'sr-only', enhanced && 'sr-only',
)} )}
onChange={handleChange}
/> />
{enhanced && ( {enhanced && (
<input
ref={dummyInputRef}
readOnly
tabIndex={-1}
onFocus={handleFocus}
className={clsx(
'bg-negative rounded-inherit w-full block',
'focus:outline-0',
'disabled:opacity-50 disabled:cursor-not-allowed',
{
'text-xxs': size === 'small',
'text-xs': size === 'medium',
},
{
'pl-4': variant === 'default',
'pl-1.5': variant === 'alternate',
},
{
'pt-4': variant === 'alternate',
},
{
'pr-4': variant === 'default' && !indicator,
'pr-1.5': variant === 'alternate' && !indicator,
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
{
'h-10': size === 'small',
'h-12': size === 'medium',
'h-16': size === 'large',
},
)}
<TagsInput
value={tags}
classNames={{
input: 'peer bg-transparent',
tag: 'text-xs p-1',
}}
onChange={handleTagsInputChange}
/> />
)} )}
{ {
@@ -164,7 +188,7 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
data-testid="label" data-testid="label"
id={labelId} id={labelId}
className={clsx( className={clsx(
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative',
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 group-focus-within:text-secondary text-primary leading-none bg-negative',
{ {
'sr-only': hiddenLabel, 'sr-only': hiddenLabel,
}, },
@@ -233,7 +257,7 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
border && ( border && (
<span <span
data-testid="border" data-testid="border"
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary"
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none group-focus-within:border-secondary"
/> />
) )
} }


+ 36
- 0
packages/web-kitchensink-reactnext/src/categories/option/react/components/TagInput/style.module.css View File

@@ -0,0 +1,36 @@
.tag-input input + div {
padding: 0.625rem;
}

.tag-input-small input + div {
padding: 0.5rem;
min-height: 2.5rem;
}

.tag-input-small input + div > input {
font-size: 0.75rem;
}

.tag-input-small input + div {
gap: 0.25rem;
}

.tag-input-medium input + div {
gap: 0.375rem;
min-height: 3rem;
}

.tag-input-large input + div {
gap: 0.375rem;
min-height: 4rem;
}

.tag-input input + div > span {
padding: 0.25rem;
border-radius: 0.25rem;
line-height: 1;
}

.tag-input input + div > input {

}

+ 4
- 1
packages/web-kitchensink-reactnext/src/pages/categories/option/index.tsx View File

@@ -1460,11 +1460,14 @@ const OptionPage: NextPage<Props> = ({
<div> <div>
<Option.TagInput <Option.TagInput
size="small" size="small"
label="MultilineTextInput"
label="change"
hint="Type anything here&hellip;" hint="Type anything here&hellip;"
indicator="A" indicator="A"
block block
enhanced enhanced
onChange={(e) => {
console.log(e.currentTarget, e.currentTarget.value, typeof e.currentTarget.value);
}}
/> />
</div> </div>
<div> <div>


+ 11
- 0
packages/web-kitchensink-reactnext/src/styles/globals.css View File

@@ -96,6 +96,17 @@
} }
} }


:root .rti--container {
--rti-bg: rgb(var(--color-negative));
--rti-border: #ccc;
--rti-main: transparent;
--rti-radius: 0;
--rti-s: 0.5rem;
--rti-tag: rgb(var(--color-positive) / 25%);
--rti-tag-remove: #e53e3e;
--rti-tag-padding: 0 0;
}

.highlight .token.number { color: rgb(var(--color-code-number)); } .highlight .token.number { color: rgb(var(--color-code-number)); }
.highlight .token.keyword { color: rgb(var(--color-code-keyword)); } .highlight .token.keyword { color: rgb(var(--color-code-keyword)); }
.highlight .token.tag { color: rgb(var(--color-code-keyword)); } .highlight .token.tag { color: rgb(var(--color-code-keyword)); }


Loading…
Cancel
Save