Browse Source

Attempt to make tag input work

Integrate tag input styling to tag input component.
pull/1/head
TheoryOfNekomata 11 months 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-dom": "18.2.0",
"react-refractor": "^2.1.7",
"react-tag-input-component": "^2.0.2",
"tailwindcss": "3.3.2",
"typescript": "5.1.3",
"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:
autoInstallPeers: true
@@ -56,6 +56,9 @@ dependencies:
react-refractor:
specifier: ^2.1.7
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:
specifier: 3.3.2
version: 3.3.2
@@ -2570,6 +2573,16 @@ packages:
unist-util-visit-parents: 3.1.1
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:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
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 { TagsInput } from 'react-tag-input-component';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import clsx from 'clsx';
import {useEnhanced} from '@/packages/react-utils';
import styles from './style.module.css';
import {delegateTriggerChangeEvent} from '@/utils/event';

type TagInputDerivedElement = HTMLInputElement;

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

/**
@@ -64,6 +68,9 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
hiddenLabel = false,
className,
enhanced: enhancedProp = false,
separator = ',',
onChange,
defaultValue,
...etcProps
}: TagInputProps,
forwardedRef,
@@ -72,9 +79,20 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
const defaultRef = React.useRef<TagInputDerivedElement>(null);
const ref = forwardedRef ?? defaultRef;
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)) {
return;
}
@@ -82,80 +100,86 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
if (!input) {
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 (
<div
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',
{
'block': block,
'inline-block align-middle': !block,
},
{
'min-h-10': size === 'small',
'min-h-12': size === 'medium',
'min-h-16': size === 'large',
},
className,
)}
>
<input
{...etcProps}
ref={ref}
readOnly={enhanced}
aria-labelledby={labelId}
type={type}
data-testid="input"
onChange={onChange}
className={clsx(
'peer',
inputClassName,
!enhanced && {
'h-10': size === 'small',
'h-12': size === 'medium',
'h-16': size === 'large',
},
!enhanced && 'peer',
enhanced && 'sr-only',
)}
onChange={handleChange}
/>
{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"
id={labelId}
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,
},
@@ -233,7 +257,7 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>(
border && (
<span
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>
<Option.TagInput
size="small"
label="MultilineTextInput"
label="change"
hint="Type anything here&hellip;"
indicator="A"
block
enhanced
onChange={(e) => {
console.log(e.currentTarget, e.currentTarget.value, typeof e.currentTarget.value);
}}
/>
</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.keyword { color: rgb(var(--color-code-keyword)); }
.highlight .token.tag { color: rgb(var(--color-code-keyword)); }


Loading…
Cancel
Save