Browse Source

Implement most components

Implement components from the ground up using Tailwind styling.
pull/1/head
TheoryOfNekomata 11 months ago
parent
commit
e1a82ed558
100 changed files with 4822 additions and 27303 deletions
  1. +0
    -0
      packages/web-kitchensink-reactnext/.eslintrc.json
  2. +0
    -1
      packages/web-kitchensink-reactnext/.gitignore
  3. +4
    -0
      packages/web-kitchensink-reactnext/README.md
  4. +0
    -1
      packages/web-kitchensink-reactnext/next.config.js
  5. +36
    -0
      packages/web-kitchensink-reactnext/package.json
  6. +2777
    -0
      packages/web-kitchensink-reactnext/pnpm-lock.yaml
  7. +0
    -0
      packages/web-kitchensink-reactnext/postcss.config.js
  8. +0
    -0
      packages/web-kitchensink-reactnext/public/favicon.ico
  9. +1
    -0
      packages/web-kitchensink-reactnext/public/next.svg
  10. +1
    -0
      packages/web-kitchensink-reactnext/public/vercel.svg
  11. +5
    -0
      packages/web-kitchensink-reactnext/src/base/button/index.ts
  12. +5
    -0
      packages/web-kitchensink-reactnext/src/base/selectcontrol/index.ts
  13. +5
    -0
      packages/web-kitchensink-reactnext/src/base/textcontrol/index.ts
  14. +115
    -0
      packages/web-kitchensink-reactnext/src/categories/action/react/components/ActionButton/index.tsx
  15. +0
    -0
      packages/web-kitchensink-reactnext/src/categories/action/react/index.ts
  16. +11
    -70
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/AudioFilePreview/index.tsx
  17. +26
    -0
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/AudioMiniFilePreview/index.tsx
  18. +2
    -2
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/BinaryFilePreview/index.tsx
  19. +64
    -0
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/FilePreview/index.tsx
  20. +19
    -105
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/FileSelectBox/index.tsx
  21. +2
    -2
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/ImageFilePreview/index.tsx
  22. +2
    -2
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/TextFilePreview/index.tsx
  23. +2
    -2
      packages/web-kitchensink-reactnext/src/categories/blob/react/components/VideoFilePreview/index.tsx
  24. +24
    -17
      packages/web-kitchensink-reactnext/src/categories/blob/react/hooks/audio/index.ts
  25. +8
    -0
      packages/web-kitchensink-reactnext/src/categories/blob/react/index.ts
  26. +198
    -0
      packages/web-kitchensink-reactnext/src/categories/freeform/react/components/MaskedTextInput/index.tsx
  27. +211
    -0
      packages/web-kitchensink-reactnext/src/categories/freeform/react/components/MultilineTextInput/index.tsx
  28. +203
    -0
      packages/web-kitchensink-reactnext/src/categories/freeform/react/components/TextInput/index.tsx
  29. +1
    -1
      packages/web-kitchensink-reactnext/src/categories/freeform/react/index.ts
  30. +35
    -0
      packages/web-kitchensink-reactnext/src/categories/information/react/components/Badge/index.tsx
  31. +0
    -0
      packages/web-kitchensink-reactnext/src/categories/information/react/index.ts
  32. +108
    -0
      packages/web-kitchensink-reactnext/src/categories/navigation/react/components/LinkButton/index.tsx
  33. +0
    -0
      packages/web-kitchensink-reactnext/src/categories/navigation/react/index.ts
  34. +5
    -0
      packages/web-kitchensink-reactnext/src/categories/option/react/components/DropdownSelect/index.tsx
  35. +1
    -0
      packages/web-kitchensink-reactnext/src/categories/option/react/index.ts
  36. +3
    -1
      packages/web-kitchensink-reactnext/src/components/DefaultLayout/index.tsx
  37. +25
    -0
      packages/web-kitchensink-reactnext/src/components/Section/index.tsx
  38. +11
    -0
      packages/web-kitchensink-reactnext/src/pages/_app.tsx
  39. +13
    -0
      packages/web-kitchensink-reactnext/src/pages/_document.tsx
  40. +13
    -0
      packages/web-kitchensink-reactnext/src/pages/api/hello.ts
  41. +29
    -35
      packages/web-kitchensink-reactnext/src/pages/categories/action/index.tsx
  42. +18
    -0
      packages/web-kitchensink-reactnext/src/pages/categories/blob/index.tsx
  43. +1
    -1
      packages/web-kitchensink-reactnext/src/pages/categories/code/index.tsx
  44. +617
    -0
      packages/web-kitchensink-reactnext/src/pages/categories/freeform/index.tsx
  45. +43
    -53
      packages/web-kitchensink-reactnext/src/pages/categories/navigation/index.tsx
  46. +0
    -0
      packages/web-kitchensink-reactnext/src/pages/categories/number/index.tsx
  47. +42
    -42
      packages/web-kitchensink-reactnext/src/pages/categories/option/index.tsx
  48. +0
    -0
      packages/web-kitchensink-reactnext/src/pages/categories/presentation/index.tsx
  49. +2
    -3
      packages/web-kitchensink-reactnext/src/pages/examples/registration-form/index.tsx
  50. +7
    -10
      packages/web-kitchensink-reactnext/src/pages/index.tsx
  51. +41
    -0
      packages/web-kitchensink-reactnext/src/styles/globals.css
  52. +0
    -0
      packages/web-kitchensink-reactnext/src/styles/kitchen-sink.css
  53. +0
    -0
      packages/web-kitchensink-reactnext/src/utils/audio.ts
  54. +0
    -0
      packages/web-kitchensink-reactnext/src/utils/blob.ts
  55. +0
    -0
      packages/web-kitchensink-reactnext/src/utils/event.ts
  56. +0
    -0
      packages/web-kitchensink-reactnext/src/utils/image.ts
  57. +0
    -0
      packages/web-kitchensink-reactnext/src/utils/numeral.ts
  58. +0
    -0
      packages/web-kitchensink-reactnext/src/utils/text.ts
  59. +54
    -0
      packages/web-kitchensink-reactnext/tailwind.config.js
  60. +32
    -0
      packages/web-kitchensink-reactnext/tsconfig.json
  61. +0
    -71
      packages/web/base/badge/package.json
  62. +0
    -3
      packages/web/base/badge/pridepack.json
  63. +0
    -45
      packages/web/base/badge/src/index.ts
  64. +0
    -9
      packages/web/base/badge/tsconfig.json
  65. +0
    -3413
      packages/web/base/badge/yarn.lock
  66. +0
    -71
      packages/web/base/blob/package.json
  67. +0
    -3
      packages/web/base/blob/pridepack.json
  68. +0
    -127
      packages/web/base/blob/src/index.ts
  69. +0
    -9
      packages/web/base/blob/tsconfig.json
  70. +0
    -3413
      packages/web/base/blob/yarn.lock
  71. +0
    -71
      packages/web/base/button/package.json
  72. +0
    -3
      packages/web/base/button/pridepack.json
  73. +0
    -318
      packages/web/base/button/src/index.ts
  74. +0
    -9
      packages/web/base/button/tsconfig.json
  75. +0
    -3413
      packages/web/base/button/yarn.lock
  76. +0
    -71
      packages/web/base/checkcontrol/package.json
  77. +0
    -3
      packages/web/base/checkcontrol/pridepack.json
  78. +0
    -346
      packages/web/base/checkcontrol/src/index.ts
  79. +0
    -9
      packages/web/base/checkcontrol/tsconfig.json
  80. +0
    -3413
      packages/web/base/checkcontrol/yarn.lock
  81. +0
    -67
      packages/web/base/selectcontrol/package.json
  82. +0
    -3
      packages/web/base/selectcontrol/pridepack.json
  83. +0
    -5
      packages/web/base/selectcontrol/src/index.ts
  84. +0
    -9
      packages/web/base/selectcontrol/tsconfig.json
  85. +0
    -3380
      packages/web/base/selectcontrol/yarn.lock
  86. +0
    -71
      packages/web/base/textcontrol/package.json
  87. +0
    -3
      packages/web/base/textcontrol/pridepack.json
  88. +0
    -405
      packages/web/base/textcontrol/src/index.ts
  89. +0
    -9
      packages/web/base/textcontrol/tsconfig.json
  90. +0
    -3413
      packages/web/base/textcontrol/yarn.lock
  91. +0
    -82
      packages/web/categories/action/react/package.json
  92. +0
    -3
      packages/web/categories/action/react/pridepack.json
  93. +0
    -4
      packages/web/categories/action/react/setupTests.ts
  94. +0
    -190
      packages/web/categories/action/react/src/components/ActionButton/ActionButton.test.tsx
  95. +0
    -172
      packages/web/categories/action/react/src/components/ActionButton/index.tsx
  96. +0
    -9
      packages/web/categories/action/react/src/web-action-react.test.ts
  97. +0
    -11
      packages/web/categories/action/react/tsconfig.json
  98. +0
    -9
      packages/web/categories/action/react/vite.config.ts
  99. +0
    -4209
      packages/web/categories/action/react/yarn.lock
  100. +0
    -81
      packages/web/categories/blob/react/package.json

packages/web/kitchen-sink/react-next/.eslintrc.json → packages/web-kitchensink-reactnext/.eslintrc.json View File


packages/web/kitchen-sink/react-next/.gitignore → packages/web-kitchensink-reactnext/.gitignore View File

@@ -23,7 +23,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

packages/web/kitchen-sink/react-next/README.md → packages/web-kitchensink-reactnext/README.md View File

@@ -8,6 +8,8 @@ First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
@@ -18,6 +20,8 @@ You can start editing the page by modifying `pages/index.tsx`. The page auto-upd

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

packages/web/kitchen-sink/react-next/next.config.js → packages/web-kitchensink-reactnext/next.config.js View File

@@ -1,7 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}

module.exports = nextConfig

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

@@ -0,0 +1,36 @@
{
"name": "web-kitchensink-reactnext",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@reach/slider": "^0.18.0",
"@types/node": "20.3.1",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
"autoprefixer": "10.4.14",
"clsx": "^1.2.1",
"eslint": "8.43.0",
"eslint-config-next": "13.4.7",
"languagedetect": "^2.0.0",
"mime-types": "^2.1.35",
"next": "13.4.7",
"postcss": "8.4.24",
"prismjs": "^1.29.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.2",
"typescript": "5.1.3",
"wavesurfer.js": "^6.6.4"
},
"devDependencies": {
"@types/mime-types": "^2.1.1",
"@types/prismjs": "^1.26.0",
"@types/wavesurfer.js": "^6.0.6"
}
}

+ 2777
- 0
packages/web-kitchensink-reactnext/pnpm-lock.yaml
File diff suppressed because it is too large
View File


packages/web/kitchen-sink/react-next/postcss.config.js → packages/web-kitchensink-reactnext/postcss.config.js View File


packages/web/kitchen-sink/react-next/public/favicon.ico → packages/web-kitchensink-reactnext/public/favicon.ico View File


+ 1
- 0
packages/web-kitchensink-reactnext/public/next.svg View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

+ 1
- 0
packages/web-kitchensink-reactnext/public/vercel.svg View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

+ 5
- 0
packages/web-kitchensink-reactnext/src/base/button/index.ts View File

@@ -0,0 +1,5 @@
export type ButtonType = 'submit' | 'reset' | 'button';

export type ButtonVariant = 'bare' | 'filled' | 'outline';

export type ButtonSize = 'small' | 'medium' | 'large';

+ 5
- 0
packages/web-kitchensink-reactnext/src/base/selectcontrol/index.ts View File

@@ -0,0 +1,5 @@
export interface SelectOption {
label: string;
value?: string;
children?: SelectOption[];
}

+ 5
- 0
packages/web-kitchensink-reactnext/src/base/textcontrol/index.ts View File

@@ -0,0 +1,5 @@
export type TextControlSize = 'small' | 'medium' | 'large';

export type TextControlVariant = 'default' | 'alternate';

export type TextControlInputType = 'text' | 'search';

+ 115
- 0
packages/web-kitchensink-reactnext/src/categories/action/react/components/ActionButton/index.tsx View File

@@ -0,0 +1,115 @@
import * as React from 'react';
import clsx from 'clsx';
import * as ButtonBase from '@tesseract-design/web-base-button';

type ActionButtonDerivedElement = HTMLButtonElement;

export interface ActionButtonProps extends Omit<React.HTMLProps<ActionButtonDerivedElement>, 'type' | 'size'> {
type?: ButtonBase.ButtonType;
variant: ButtonBase.ButtonVariant;
block?: boolean;
subtext?: React.ReactNode;
badge?: React.ReactNode;
menuItem?: boolean;
size?: ButtonBase.ButtonSize;
compact?: boolean;
}

export const ActionButton = React.forwardRef<ActionButtonDerivedElement, ActionButtonProps>(({
type = 'button' as const,
variant,
subtext,
badge,
menuItem = false,
children,
size = 'medium' as const,
compact = false,
className,
block = false,
...etcProps
}, forwardedRef) => (
<button
{...etcProps}
type={type}
ref={forwardedRef}
className={clsx(
'items-center justify-center rounded overflow-hidden ring-secondary/50 leading-none gap-4 select-none',
'focus:outline-0 focus:ring-4',
'active:ring-tertiary/50',
'disabled:opacity-50 disabled:cursor-not-allowed',
{
'flex w-full': block,
'inline-flex max-w-full align-middle': !block,
},
{
'pl-2': compact,
'pl-4': !compact,
'pr-4': !(compact || menuItem),
'pr-2': compact || menuItem,
},
{
'border-2 border-primary disabled:border-primary focus:border-secondary active:border-tertiary' : variant !== 'bare',
'bg-negative text-primary disabled:text-primary focus:text-secondary active:text-tertiary': variant !== 'filled',
'bg-primary text-negative disabled:bg-primary focus:bg-secondary active:bg-tertiary': variant === 'filled',
},
{
'h-10': size === 'small',
'h-12': size === 'medium',
'h-16': size === 'large',
},
className,
)}
>
<span
className={clsx(
'flex-auto min-w-0',
{
'text-left': compact || menuItem,
'text-center': !(compact || menuItem),
},
)}
>
<span
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded"
data-testid="children"
>
{children}
</span>
{subtext && (
<>
<span className="sr-only">
{' - '}
</span>
<span
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs"
data-testid="subtext"
>
{subtext}
</span>
</>
)}
</span>
{badge && (
<span
data-testid="badge"
>
{badge}
</span>
)}
{menuItem && (
<span
data-testid="menuItemIndicator"
>
<svg
className="w-6 h-6 fill-none stroke-current stroke-2 linejoin-round linecap-round"
viewBox="0 0 24 24"
role="presentation"
>
<polyline points="9 18 15 12 9 6"/>
</svg>
</span>
)}
</button>
));

ActionButton.displayName = 'ActionButton';

packages/web/categories/action/react/src/index.ts → packages/web-kitchensink-reactnext/src/categories/action/react/index.ts View File


packages/web/kitchen-sink/react-next/src/components/AudioFilePreview/index.tsx → packages/web-kitchensink-reactnext/src/categories/blob/react/components/AudioFilePreview/index.tsx View File

@@ -1,79 +1,20 @@
import * as React from 'react';
import WaveSurfer from 'wavesurfer.js';
import {AudioFile, getMimeTypeDescription} from '../../utils/blob';
import {formatFileSize, formatNumeral, formatSecondsDurationPrecise} from '../../utils/numeral';
import {AudioFile, getMimeTypeDescription} from '@/utils/blob';
import {formatFileSize, formatNumeral, formatSecondsDurationPrecise} from '@/utils/numeral';
import {useAudioFilePreviewControls} from '@/categories/blob/react/hooks/audio';

export interface VideoFilePreviewProps {
export interface AudioFilePreviewProps {
file: AudioFile;
}

export const AudioFilePreview: React.FC<VideoFilePreviewProps> = ({
export const AudioFilePreview: React.FC<AudioFilePreviewProps> = ({
file: f,
}) => {
const mediaContainerRef = React.useRef<HTMLDivElement>(null);
const mediaControllerRef = React.useRef<WaveSurfer>(null);
const [isPlaying, setIsPlaying] = React.useState(false);

React.useEffect(() => {
if (!mediaControllerRef.current) {
return;
}

if (isPlaying) {
void mediaControllerRef.current.play();
return;
}

mediaControllerRef.current.pause();
}, [isPlaying]);

React.useEffect(() => {
if (!mediaControllerRef.current) {
return;
}

if (!mediaContainerRef.current) {
return;
}

if (typeof f.metadata?.previewUrl !== 'string') {
return;
}

mediaContainerRef.current.innerHTML = '';

if (f.type === 'audio/mid') {
return;
}

const mediaControllerRefMutable = mediaControllerRef as React.MutableRefObject<WaveSurfer>;
mediaControllerRefMutable.current = WaveSurfer.create({
container: mediaContainerRef.current,
url: f.metadata.previewUrl,
cursorWidth: 0,
height: mediaContainerRef.current.offsetHeight,
barWidth: 2,
barGap: 2,
barRadius: 1,
});

mediaControllerRefMutable.current.on('finish', () => {
setIsPlaying(false);
mediaControllerRefMutable.current.seekTo(0);
});

return () => {
if (!mediaControllerRefMutable.current) {
return;
}

mediaControllerRefMutable.current.destroy();
}
}, [f, mediaContainerRef, mediaControllerRef]);

const playMedia = () => {
setIsPlaying((p) => !p);
};
const {
mediaContainerRef,
playMedia,
isPlaying,
} = useAudioFilePreviewControls({ file: f });

return (
<div className="flex flex-col gap-4 w-full h-full relative">
@@ -110,7 +51,7 @@ export const AudioFilePreview: React.FC<VideoFilePreviewProps> = ({
title={f.type}
className="m-0 w-full text-ellipsis overflow-hidden"
>
{getMimeTypeDescription(f.type)}
{getMimeTypeDescription(f.type, f.name)}
</dd>
</div>
<div className="w-full">

+ 26
- 0
packages/web-kitchensink-reactnext/src/categories/blob/react/components/AudioMiniFilePreview/index.tsx View File

@@ -0,0 +1,26 @@
import * as React from 'react';
import {AudioFile, getMimeTypeDescription} from '@/utils/blob';
import {formatFileSize, formatNumeral, formatSecondsDurationPrecise} from '@/utils/numeral';
import {useAudioFilePreviewControls} from '@/categories/blob/react/hooks/audio';

export interface AudioMiniFilePreviewProps {
file: AudioFile;
}

export const AudioMiniFilePreview: React.FC<AudioMiniFilePreviewProps> = ({
file: f,
}) => {
const {
mediaContainerRef,
playMedia,
isPlaying,
} = useAudioFilePreviewControls({ file: f });

return (
<div
className="absolute top-0 left-0 w-full h-full cursor-pointer"
ref={mediaContainerRef}
onClick={playMedia}
/>
);
};

packages/web/kitchen-sink/react-next/src/components/BinaryFilePreview/index.tsx → packages/web-kitchensink-reactnext/src/categories/blob/react/components/BinaryFilePreview/index.tsx View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import {BinaryFile, getMimeTypeDescription} from '../../utils/blob';
import {formatFileSize, formatNumeral} from '../../utils/numeral';
import {BinaryFile, getMimeTypeDescription} from '@/utils/blob';
import {formatFileSize, formatNumeral} from '@/utils/numeral';

export interface BinaryFilePreviewProps {
file: BinaryFile;

+ 64
- 0
packages/web-kitchensink-reactnext/src/categories/blob/react/components/FilePreview/index.tsx View File

@@ -0,0 +1,64 @@
import * as React from 'react';
import { TextFilePreview } from '../TextFilePreview';
import { ImageFilePreview } from '../ImageFilePreview';
import { AudioFilePreview } from '../AudioFilePreview';
import { VideoFilePreview } from '../VideoFilePreview';
import { BinaryFilePreview } from '../BinaryFilePreview';
import {AugmentedFile, augmentFile, ContentType, getContentType} from '@/utils/blob';

const FILE_PREVIEW_COMPONENTS: Record<ContentType, React.ElementType> = {
[ContentType.TEXT]: TextFilePreview,
[ContentType.IMAGE]: ImageFilePreview,
[ContentType.AUDIO]: AudioFilePreview,
[ContentType.VIDEO]: VideoFilePreview,
[ContentType.BINARY]: BinaryFilePreview,
};

const useFilePreviews = (fileList?: FileList) => {
const [selectedFiles, setSelectedFiles] = React.useState([] as AugmentedFile[]);
React.useEffect(() => {
const loadFilePreviews = async (fileList: FileList) => {
const files = Array.from(fileList);
return Promise.all(
files.map(async (f) => {
const augmentedFile = await augmentFile(f);
if (augmentedFile.resolvedType === ContentType.TEXT && augmentedFile.metadata?.scheme) {
await import(`prismjs/components/prism-${augmentedFile.metadata.scheme}`);
}
return augmentedFile;
})
);
}

if (fileList) {
loadFilePreviews(fileList).then((fileResult) => {
setSelectedFiles(fileResult);
});
}
}, [fileList]);

return React.useMemo(() => ({
files: selectedFiles,
}), [selectedFiles]);
};

export interface FilePreviewProps {
fileList?: FileList;
}

export const FilePreview: React.FC<FilePreviewProps> = ({
fileList,
}) => {
const { files } = useFilePreviews(fileList);
if (files.length < 1) {
return null;
}

const f = fileList?.[0];
const contentType = getContentType(f?.type, f?.name);
const FilePreviewComponent = FILE_PREVIEW_COMPONENTS[contentType] ?? BinaryFilePreview;

return (
<FilePreviewComponent file={f} />
);
};

packages/web/kitchen-sink/react-next/src/pages/categories/blob/index.tsx → packages/web-kitchensink-reactnext/src/categories/blob/react/components/FileSelectBox/index.tsx View File

@@ -1,23 +1,9 @@
import {NextPage} from 'next';
import * as React from 'react';
//import * as BlobReact from '@tesseract-design/web-blob-react';
import * as BlobBase from '@tesseract-design/web-base-blob';
import * as ButtonBase from '@tesseract-design/web-base-button';
import {formatFileSize} from '../../../utils/numeral';
import {
AugmentedFile,
augmentFile,
ContentType,
getContentType,
getMimeTypeDescription,
} from '../../../utils/blob';
import {delegateTriggerChangeEvent} from '../../../utils/event';
import {TextFilePreview} from '../../../components/TextFilePreview';
import {ImageFilePreview} from '../../../components/ImageFilePreview';
import {AudioFilePreview} from '../../../components/AudioFilePreview';
import {VideoFilePreview} from '../../../components/VideoFilePreview';
import {BinaryFilePreview} from '../../../components/BinaryFilePreview';
import {AudioMiniFilePreview} from '../../../components/AudioMiniFilePreview';
import {AugmentedFile, augmentFile, ContentType, getMimeTypeDescription} from '@/utils/blob';
import { FilePreview as FilePreviewComponent} from '../FilePreview';
import {formatFileSize} from '@/utils/numeral';
import {AudioMiniFilePreview} from '@tesseract-design/web-blob-react';
import {delegateTriggerChangeEvent} from '@/utils/event';

export interface FileButtonProps extends Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style' | 'label' | 'list'> {
/**
@@ -71,40 +57,6 @@ const useFilePreviews = (fileList?: FileList) => {
}), [selectedFiles]);
}

const FILE_PREVIEW_COMPONENTS: Record<ContentType, React.ElementType> = {
[ContentType.TEXT]: TextFilePreview,
[ContentType.IMAGE]: ImageFilePreview,
[ContentType.AUDIO]: AudioFilePreview,
[ContentType.VIDEO]: VideoFilePreview,
[ContentType.BINARY]: BinaryFilePreview,
};

const FilePreview = ({
fileList: fileList,
}: { fileList?: FileList }) => {
const { files } = useFilePreviews(fileList);

if (files.length < 1) {
return null;
}

const f = files[0];
const contentType = getContentType(f.type, f.name);
const FilePreviewComponent = FILE_PREVIEW_COMPONENTS[contentType] ?? BinaryFilePreview;

return (
<div
className={`w-full h-full`}
>
<div data-testid="selectedFileItem" className={`h-full w-full p-4 box-border rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10`}>
<FilePreviewComponent
file={f}
/>
</div>
</div>
)
}

const FilePreviewGrid = ({
fileList,
}: { fileList?: FileList }) => {
@@ -212,10 +164,7 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>

return (
<div
className={BlobBase.Root({
border,
block,
})}
className="block"
onDragEnter={cancelEvent}
onDragOver={cancelEvent}
onDrop={handleDropZone}
@@ -240,12 +189,7 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>
border && (
<span
data-testid="border"
className={
BlobBase.Border({
border,
block,
})
}
className="block"
/>
)
}
@@ -255,10 +199,7 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>
&& (
<div
data-testid="label"
className={BlobBase.LabelWrapper({
border,
block,
})}
className="block"
>
{label}
</div>
@@ -298,7 +239,15 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>
{
!multiple
&& (
<FilePreview fileList={fileList} />
<div
className={`w-full h-full`}
>
<div data-testid="selectedFileItem" className={`h-full w-full p-4 box-border rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10`}>
<FilePreviewComponent
fileList={fileList}
/>
</div>
</div>
)
}
</div>
@@ -306,15 +255,7 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>
<div className="pointer-events-none absolute bottom-0 left-0 w-full text-center h-12 box-border flex">
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full">
<span
className={ButtonBase.Button({
size: ButtonBase.ButtonSize.MEDIUM,
border: false,
block: true,
variant: ButtonBase.ButtonVariant.OUTLINE,
disabled,
compact: false,
menuItem: false,
})}
className=""
>
Reselect
</span>
@@ -324,15 +265,7 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>
data-testid="clear"
type="button"
onClick={deleteFiles}
className={ButtonBase.Button({
size: ButtonBase.ButtonSize.MEDIUM,
border: false,
block: true,
variant: ButtonBase.ButtonVariant.OUTLINE,
disabled,
compact: false,
menuItem: false,
})}
className=""
>
{multiple ? 'Clear' : 'Delete'}
</button>
@@ -347,22 +280,3 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>
);

FileSelectBox.displayName = 'FileSelectBox';

const BlobReact = {
FileSelectBox
}

const BlobPage: NextPage = () => {
return (
<BlobReact.FileSelectBox
border
enhanced
label="Primary Image"
hint="Select any files here"
block
onChange={(e) => console.log(e.currentTarget.files)}
/>
)
}

export default BlobPage;

packages/web/kitchen-sink/react-next/src/components/ImageFilePreview/index.tsx → packages/web-kitchensink-reactnext/src/categories/blob/react/components/ImageFilePreview/index.tsx View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import {getMimeTypeDescription, ImageFile} from '../../utils/blob';
import {formatFileSize, formatNumeral} from '../../utils/numeral';
import {getMimeTypeDescription, ImageFile} from '@/utils/blob';
import {formatFileSize, formatNumeral} from '@/utils/numeral';

export interface ImageFilePreviewProps {
file: ImageFile;

packages/web/kitchen-sink/react-next/src/components/TextFilePreview/index.tsx → packages/web-kitchensink-reactnext/src/categories/blob/react/components/TextFilePreview/index.tsx View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import Prism from 'prismjs';
import {formatFileSize, formatNumeral} from '../../utils/numeral';
import {TextFile} from '../../utils/blob';
import {formatFileSize, formatNumeral} from '@/utils/numeral';
import {TextFile} from '@/utils/blob';

export interface TextFilePreviewProps {
file: TextFile;

packages/web/kitchen-sink/react-next/src/components/VideoFilePreview/index.tsx → packages/web-kitchensink-reactnext/src/categories/blob/react/components/VideoFilePreview/index.tsx View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import {getMimeTypeDescription, VideoFile} from '../../utils/blob';
import {formatFileSize, formatNumeral, formatSecondsDurationConcise} from '../../utils/numeral';
import {getMimeTypeDescription, VideoFile} from '@/utils/blob';
import {formatFileSize, formatNumeral, formatSecondsDurationConcise} from '@/utils/numeral';

export interface VideoFilePreviewProps {
file: VideoFile;

packages/web/kitchen-sink/react-next/src/components/AudioMiniFilePreview/index.tsx → packages/web-kitchensink-reactnext/src/categories/blob/react/hooks/audio/index.ts View File

@@ -1,15 +1,12 @@
import * as React from 'react';
import WaveSurfer from 'wavesurfer.js';
import {AudioFile, getMimeTypeDescription} from '../../utils/blob';
import {formatFileSize, formatNumeral, formatSecondsDurationPrecise} from '../../utils/numeral';
import {AudioFile} from '@/utils/blob';

export interface VideoFilePreviewProps {
export interface UseAudioFilePreviewControlsOptions {
file: AudioFile;
}

export const AudioMiniFilePreview: React.FC<VideoFilePreviewProps> = ({
file: f,
}) => {
export const useAudioFilePreviewControls = ({ file: f }: UseAudioFilePreviewControlsOptions) => {
const mediaContainerRef = React.useRef<HTMLDivElement>(null);
const mediaControllerRef = React.useRef<WaveSurfer>(null);
const [isPlaying, setIsPlaying] = React.useState(false);
@@ -41,10 +38,14 @@ export const AudioMiniFilePreview: React.FC<VideoFilePreviewProps> = ({
}

mediaContainerRef.current.innerHTML = '';

if (f.type === 'audio/mid') {
return;
}

const mediaControllerRefMutable = mediaControllerRef as React.MutableRefObject<WaveSurfer>;
mediaControllerRefMutable.current = WaveSurfer.create({
container: mediaContainerRef.current,
url: f.metadata.previewUrl,
cursorWidth: 0,
height: mediaContainerRef.current.offsetHeight,
barWidth: 2,
@@ -57,6 +58,10 @@ export const AudioMiniFilePreview: React.FC<VideoFilePreviewProps> = ({
mediaControllerRefMutable.current.seekTo(0);
});

// TODO get the preview URL here

mediaControllerRefMutable.current.load(f.metadata.previewUrl);

return () => {
if (!mediaControllerRefMutable.current) {
return;
@@ -66,15 +71,17 @@ export const AudioMiniFilePreview: React.FC<VideoFilePreviewProps> = ({
}
}, [f, mediaContainerRef, mediaControllerRef]);

const playMedia = () => {
const playMedia = React.useCallback(() => {
setIsPlaying((p) => !p);
};

return (
<div
className="absolute top-0 left-0 w-full h-full cursor-pointer"
ref={mediaContainerRef}
onClick={playMedia}
/>
);
}, []);

return React.useMemo(() => ({
mediaContainerRef,
playMedia,
isPlaying,
}), [
mediaContainerRef,
playMedia,
isPlaying,
]);
};

+ 8
- 0
packages/web-kitchensink-reactnext/src/categories/blob/react/index.ts View File

@@ -0,0 +1,8 @@
export * from './components/AudioFilePreview';
export * from './components/AudioMiniFilePreview';
export * from './components/BinaryFilePreview';
export * from './components/BinaryFilePreview';
export * from './components/FileSelectBox';
export * from './components/ImageFilePreview';
export * from './components/TextFilePreview';
export * from './components/VideoFilePreview';

+ 198
- 0
packages/web-kitchensink-reactnext/src/categories/freeform/react/components/MaskedTextInput/index.tsx View File

@@ -0,0 +1,198 @@
import * as React from 'react';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import clsx from 'clsx';

type MaskedTextInputDerivedElement = HTMLInputElement;

export interface MaskedTextInputProps extends Omit<React.HTMLProps<MaskedTextInputDerivedElement>, 'size' | 'type' | 'label'> {
/**
* Short textual description indicating the nature of the component's value.
*/
label?: React.ReactNode,
/**
* Short textual description as guidelines for valid input values.
*/
hint?: React.ReactNode,
/**
* Size of the component.
*/
size?: TextControlBase.TextControlSize,
/**
* Additional description, usually graphical, indicating the nature of the component's value.
*/
indicator?: React.ReactNode,
/**
* Should the component display a border?
*/
border?: boolean,
/**
* Should the component occupy the whole width of its parent?
*/
block?: boolean,
/**
* Style of the component.
*/
variant?: TextControlBase.TextControlVariant,
/**
* Is the label hidden?
*/
hiddenLabel?: boolean,
}

/**
* Component for inputting textual values.
*
* This component supports multiline input and adjusts its layout accordingly.
*/
export const MaskedTextInput = React.forwardRef<MaskedTextInputDerivedElement, MaskedTextInputProps>(
(
{
label,
hint,
indicator,
size = 'medium' as const,
border = false,
block = false,
variant = 'default' as const,
hiddenLabel = false,
className,
...etcProps
}: MaskedTextInputProps,
ref,
) => {
const labelId = React.useId();

return (
<div
className={clsx(
'relative rounded ring-secondary/50',
'focus-within:ring-4',
{
'block': block,
'inline-block align-middle': !block,
},
className,
)}
>
<input
{...etcProps}
ref={ref}
aria-labelledby={labelId}
type="password"
data-testid="input"
className={clsx(
'bg-negative rounded-inherit w-full peer 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',
},
)}
/>
{
label && (
<div
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',
{
'sr-only': hiddenLabel,
},
{
'pr-1': !indicator,
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
{label}
</div>
</div>
)
}
{hint && (
<div
data-testid="hint"
className={clsx(
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative',
{
'bottom-0 pl-4 pb-1': variant === 'default',
'top-0.5': variant === 'alternate',
},
{
'pt-2': variant === 'alternate' && size === 'small',
'pt-3': variant === 'alternate' && size !== 'small',
},
{
'pr-4': !indicator && variant === 'default',
'pr-1': !indicator && variant === 'alternate',
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis"
>
{hint}
</div>
</div>
)}
{indicator && (
<div
className={clsx(
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none',
{
'w-10': size === 'small',
'w-12': size === 'medium',
'w-16': size === 'large',
},
)}
>
{indicator}
</div>
)}
{
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"
/>
)
}
</div>
);
},
);

MaskedTextInput.displayName = 'MaskedTextInput';

+ 211
- 0
packages/web-kitchensink-reactnext/src/categories/freeform/react/components/MultilineTextInput/index.tsx View File

@@ -0,0 +1,211 @@
import * as React from 'react';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import clsx from 'clsx';

type MultilineTextInputDerivedElement = HTMLTextAreaElement;

export interface MultilineTextInputProps extends Omit<React.HTMLProps<MultilineTextInputDerivedElement>, 'size' | 'style' | 'label'> {
/**
* Short textual description indicating the nature of the component's value.
*/
label?: React.ReactNode,
/**
* Short textual description as guidelines for valid input values.
*/
hint?: React.ReactNode,
/**
* Size of the component.
*/
size?: TextControlBase.TextControlSize,
/**
* Additional description, usually graphical, indicating the nature of the component's value.
*/
indicator?: React.ReactNode,
/**
* Should the component display a border?
*/
border?: boolean,
/**
* Should the component occupy the whole width of its parent?
*/
block?: boolean,
/**
* Style of the component.
*/
variant?: TextControlBase.TextControlVariant,
/**
* Is the label hidden?
*/
hiddenLabel?: boolean,
}

/**
* Component for inputting textual values.
*
* This component supports multiline input and adjusts its layout accordingly.
*/
export const MultilineTextInput = React.forwardRef<MultilineTextInputDerivedElement, MultilineTextInputProps>(
(
{
label,
hint,
indicator,
size = 'medium' as const,
border = false,
block = false,
variant = 'default' as const,
hiddenLabel = false,
className,
...etcProps
}: MultilineTextInputProps,
ref,
) => {
const labelId = React.useId();

return (
<div
className={clsx(
'relative rounded ring-secondary/50',
'focus-within:ring-4',
{
'block': block,
'inline-block align-middle': !block,
},
className,
)}
>
<textarea
{...etcProps}
ref={ref}
aria-labelledby={labelId}
data-testid="input"
style={{
height: 0,
}}
className={clsx(
'bg-negative rounded-inherit w-full peer block',
'focus:outline-0',
'disabled:opacity-50 disabled:cursor-not-allowed',
{
'resize': !block,
'resize-y': block,
},
{
'text-xxs': size === 'small',
'text-xs': size === 'medium',
},
{
'pl-4': variant === 'default',
'pl-1.5': variant === 'alternate',
},
{
'pt-4': variant === 'alternate' && size === 'small',
'pt-5': variant === 'alternate' && size === 'medium',
'pt-8': variant === 'alternate' && size === 'large',
},
{
'py-2.5': variant === 'default' && size === 'small',
'py-3': variant === 'default' && size === 'medium',
'py-5': variant === 'default' && size === 'large',
},
{
'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',
},
{
'min-h-10': size === 'small',
'min-h-12': size === 'medium',
'min-h-16': size === 'large',
},
)}
/>
{
label && (
<div
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',
{
'sr-only': hiddenLabel,
},
{
'pr-1': !indicator,
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
{label}
</div>
</div>
)
}
{hint && (
<div
data-testid="hint"
className={clsx(
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative',
{
'bottom-0 pl-4 pb-1': variant === 'default',
'top-0.5': variant === 'alternate',
},
{
'pt-2': variant === 'alternate' && size === 'small',
'pt-3': variant === 'alternate' && size !== 'small',
},
{
'pr-4': !indicator && variant === 'default',
'pr-1': !indicator && variant === 'alternate',
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis"
>
{hint}
</div>
</div>
)}
{indicator && (
<div
className={clsx(
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none',
{
'w-10': size === 'small',
'w-12': size === 'medium',
'w-16': size === 'large',
},
)}
>
{indicator}
</div>
)}
{
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"
/>
)
}
</div>
);
}
);

MultilineTextInput.displayName = 'MultilineTextInput';

+ 203
- 0
packages/web-kitchensink-reactnext/src/categories/freeform/react/components/TextInput/index.tsx View File

@@ -0,0 +1,203 @@
import * as React from 'react';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import clsx from 'clsx';

type TextInputDerivedElement = HTMLInputElement;

export interface TextInputProps extends Omit<React.HTMLProps<TextInputDerivedElement>, 'size' | 'type' | 'style' | 'label' | 'list'> {
/**
* Short textual description indicating the nature of the component's value.
*/
label?: React.ReactNode,
/**
* Short textual description as guidelines for valid input values.
*/
hint?: React.ReactNode,
/**
* Size of the component.
*/
size?: TextControlBase.TextControlSize,
/**
* Additional description, usually graphical, indicating the nature of the component's value.
*/
indicator?: React.ReactNode,
/**
* Should the component display a border?
*/
border?: boolean,
/**
* Should the component occupy the whole width of its parent?
*/
block?: boolean,
/**
* Type of the component value.
*/
type?: TextControlBase.TextControlInputType,
/**
* Style of the component.
*/
variant?: TextControlBase.TextControlVariant,
/**
* Is the label hidden?
*/
hiddenLabel?: boolean,
}

/**
* Component for inputting textual values.
*
* This component supports multiline input and adjusts its layout accordingly.
*/
export const TextInput = React.forwardRef<TextInputDerivedElement, TextInputProps>(
(
{
label = '',
hint = '',
indicator,
size = 'medium' as const,
border = false,
block = false,
type = 'text' as const,
variant = 'default' as const,
hiddenLabel = false,
className,
...etcProps
}: TextInputProps,
ref,
) => {
const labelId = React.useId();

return (
<div
className={clsx(
'relative rounded ring-secondary/50',
'focus-within:ring-4',
{
'block': block,
'inline-block align-middle': !block,
},
className,
)}
>
<input
{...etcProps}
ref={ref}
aria-labelledby={labelId}
type={type}
data-testid="input"
className={clsx(
'bg-negative rounded-inherit w-full peer 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',
},
)}
/>
{
label && (
<div
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',
{
'sr-only': hiddenLabel,
},
{
'pr-1': !indicator,
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
{label}
</div>
</div>
)
}
{hint && (
<div
data-testid="hint"
className={clsx(
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative',
{
'bottom-0 pl-4 pb-1': variant === 'default',
'top-0.5': variant === 'alternate',
},
{
'pt-2': variant === 'alternate' && size === 'small',
'pt-3': variant === 'alternate' && size !== 'small',
},
{
'pr-4': !indicator && variant === 'default',
'pr-1': !indicator && variant === 'alternate',
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis"
>
{hint}
</div>
</div>
)}
{indicator && (
<div
className={clsx(
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none',
{
'w-10': size === 'small',
'w-12': size === 'medium',
'w-16': size === 'large',
},
)}
>
{indicator}
</div>
)}
{
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"
/>
)
}
</div>
);
}
);

TextInput.displayName = 'TextInput';

packages/web/categories/freeform/react/src/index.ts → packages/web-kitchensink-reactnext/src/categories/freeform/react/index.ts View File


+ 35
- 0
packages/web-kitchensink-reactnext/src/categories/information/react/components/Badge/index.tsx View File

@@ -0,0 +1,35 @@
import * as React from 'react';
import clsx from 'clsx';

type BadgeDerivedElement = HTMLSpanElement;

export interface BadgeProps extends React.HTMLProps<BadgeDerivedElement> {
rounded?: boolean;
}

export const Badge = React.forwardRef<BadgeDerivedElement, BadgeProps>(({
children,
rounded = false,
className,
...etcProps
}, forwardedRef) => (
<span
{...etcProps}
ref={forwardedRef}
className={clsx(
'relative h-6 min-w-6 flex items-center justify-center text-xs font-bold overflow-hidden font-semi-expanded',
'before:absolute before:top-0 before:left-0 before:w-full before:h-full before:bg-current before:opacity-25',
{
'rounded-full px-2': rounded,
'rounded px-1': !rounded,
},
className,
)}
>
<span className="relative w-full">
{children}
</span>
</span>
));

Badge.displayName = 'Badge';

packages/web/categories/information/react/src/index.ts → packages/web-kitchensink-reactnext/src/categories/information/react/index.ts View File


+ 108
- 0
packages/web-kitchensink-reactnext/src/categories/navigation/react/components/LinkButton/index.tsx View File

@@ -0,0 +1,108 @@
import * as React from 'react';
import clsx from 'clsx';
import * as ButtonBase from '@tesseract-design/web-base-button';

type LinkButtonDerivedElement = HTMLAnchorElement;

export interface LinkButtonProps extends Omit<React.HTMLProps<LinkButtonDerivedElement>, 'size'> {
block?: boolean;
variant: ButtonBase.ButtonVariant;
subtext?: React.ReactNode;
badge?: React.ReactNode;
menuItem?: boolean;
size?: ButtonBase.ButtonSize;
compact?: boolean;
component?: React.ElementType;
}

export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonProps>(({
variant,
subtext,
badge,
menuItem = false,
children,
size = 'medium' as const,
compact = false,
className,
block = false,
component: Component = 'a',
...etcProps
}, forwardedRef) => (
<Component
{...etcProps}
ref={forwardedRef}
className={clsx(
'items-center justify-center rounded overflow-hidden ring-secondary/50 leading-none gap-4 select-none',
'focus:outline-0 focus:ring-4',
'active:ring-tertiary/50',
{
'flex w-full': block,
'inline-flex max-w-full align-middle': !block,
},
{
'pl-2': compact,
'pl-4': !compact,
'pr-4': !(compact || menuItem),
'pr-2': compact || menuItem,
},
{
'border-2 border-primary focus:border-secondary active:border-tertiary' : variant !== 'bare',
'bg-negative text-primary focus:text-secondary active:text-tertiary': variant !== 'filled',
'bg-primary text-negative focus:bg-secondary active:bg-tertiary': variant === 'filled',
},
{
'h-10': size === 'small',
'h-12': size === 'medium',
'h-16': size === 'large',
},
className,
)}
>
<span
className={clsx(
'flex-auto min-w-0',
{
'text-left': compact || menuItem,
'text-center': !(compact || menuItem),
},
)}
>
<span
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded"
data-testid="children"
>
{children}
</span>
{subtext && (
<span
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs"
data-testid="subtext"
>
{subtext}
</span>
)}
</span>
{badge && (
<span
data-testid="badge"
>
{badge}
</span>
)}
{menuItem && (
<span
data-testid="menuItemIndicator"
>
<svg
className="w-6 h-6 fill-none stroke-current stroke-2 linejoin-round linecap-round"
viewBox="0 0 24 24"
role="presentation"
>
<polyline points="9 18 15 12 9 6"/>
</svg>
</span>
)}
</Component>
));

LinkButton.displayName = 'LinkButton';

packages/web/categories/navigation/react/src/index.ts → packages/web-kitchensink-reactnext/src/categories/navigation/react/index.ts View File


+ 5
- 0
packages/web-kitchensink-reactnext/src/categories/option/react/components/DropdownSelect/index.tsx View File

@@ -0,0 +1,5 @@
export const DropdownSelect = () => {
return (
<select />
)
};

+ 1
- 0
packages/web-kitchensink-reactnext/src/categories/option/react/index.ts View File

@@ -0,0 +1 @@
export * from './components/DropdownSelect';

packages/web/kitchen-sink/react-next/src/components/DefaultLayout/index.tsx → packages/web-kitchensink-reactnext/src/components/DefaultLayout/index.tsx View File

@@ -25,7 +25,9 @@ export const DefaultLayout: FC<DefaultLayoutProps> = ({
)
}
</Head>
{children}
<main className="mt-8 mb-16 md:mt-16 md:mb-32">
{children}
</main>
</>
)
}

+ 25
- 0
packages/web-kitchensink-reactnext/src/components/Section/index.tsx View File

@@ -0,0 +1,25 @@
export const Section = ({ children, title }) => (
<section>
<div className="container mx-auto px-4">
<h1>
{title}
</h1>
<div>
{children}
</div>
</div>
</section>
);

export const Subsection = ({ children, title }) => (
<section>
<div>
<h2>
{title}
</h2>
<div>
{children}
</div>
</div>
</section>
);

+ 11
- 0
packages/web-kitchensink-reactnext/src/pages/_app.tsx View File

@@ -0,0 +1,11 @@
import '@/styles/globals.css'
import '@/styles/kitchen-sink.css'
import type { AppProps } from 'next/app'

const App = ({ Component, pageProps }: AppProps) => {
return (
<Component {...pageProps} />
);
};

export default App;

+ 13
- 0
packages/web-kitchensink-reactnext/src/pages/_document.tsx View File

@@ -0,0 +1,13 @@
import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
return (
<Html lang="en" className="bg-negative text-positive">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

+ 13
- 0
packages/web-kitchensink-reactnext/src/pages/api/hello.ts View File

@@ -0,0 +1,13 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'

type Data = {
name: string
}

export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}

packages/web/kitchen-sink/react-next/src/pages/categories/action/index.tsx → packages/web-kitchensink-reactnext/src/pages/categories/action/index.tsx View File

@@ -1,7 +1,7 @@
import { NextPage } from 'next';
import { ButtonSize, ButtonVariant } from '@tesseract-design/web-base-button';
import * as Action from '@tesseract-design/web-action-react';
import { DefaultLayout } from 'src/components/DefaultLayout';
import { DefaultLayout } from '@/components/DefaultLayout';

const ActionPage: NextPage = () => {
return (
@@ -21,6 +21,7 @@ const ActionPage: NextPage = () => {
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 my-4">
<div>
<Action.ActionButton
variant="bare"
block
>
Button
@@ -28,7 +29,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Action.ActionButton
variant={ButtonVariant.FILLED}
variant="filled"
block
>
Button
@@ -36,7 +37,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Action.ActionButton
border
variant="outline"
block
>
Button
@@ -44,8 +45,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Action.ActionButton
border
variant={ButtonVariant.FILLED}
variant="filled"
block
>
Button
@@ -53,6 +53,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Action.ActionButton
variant="bare"
block
disabled
>
@@ -61,7 +62,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Action.ActionButton
variant={ButtonVariant.FILLED}
variant="filled"
block
disabled
>
@@ -70,7 +71,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Action.ActionButton
border
variant="outline"
block
disabled
>
@@ -79,8 +80,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Action.ActionButton
border
variant={ButtonVariant.FILLED}
variant="filled"
block
disabled
>
@@ -97,8 +97,8 @@ const ActionPage: NextPage = () => {
<div>
<Action.ActionButton
block
border
size={ButtonSize.SMALL}
variant="outline"
size="small"
>
Button
</Action.ActionButton>
@@ -106,9 +106,8 @@ const ActionPage: NextPage = () => {
<div>
<Action.ActionButton
block
border
variant={ButtonVariant.FILLED}
size={ButtonSize.SMALL}
variant="filled"
size="small"
>
Button
</Action.ActionButton>
@@ -116,8 +115,8 @@ const ActionPage: NextPage = () => {
<div>
<Action.ActionButton
block
border
size={ButtonSize.MEDIUM}
variant="outline"
size="medium"
>
Button
</Action.ActionButton>
@@ -125,9 +124,8 @@ const ActionPage: NextPage = () => {
<div>
<Action.ActionButton
block
border
variant={ButtonVariant.FILLED}
size={ButtonSize.MEDIUM}
variant="filled"
size="medium"
>
Button
</Action.ActionButton>
@@ -135,8 +133,8 @@ const ActionPage: NextPage = () => {
<div>
<Action.ActionButton
block
border
size={ButtonSize.LARGE}
variant="outline"
size="large"
>
Button
</Action.ActionButton>
@@ -144,9 +142,8 @@ const ActionPage: NextPage = () => {
<div>
<Action.ActionButton
block
border
variant={ButtonVariant.FILLED}
size={ButtonSize.LARGE}
variant="filled"
size="large"
>
Button
</Action.ActionButton>
@@ -162,7 +159,7 @@ const ActionPage: NextPage = () => {
<Action.ActionButton
block
compact
border
variant="outline"
>
Button
</Action.ActionButton>
@@ -171,8 +168,7 @@ const ActionPage: NextPage = () => {
<Action.ActionButton
block
compact
border
variant={ButtonVariant.FILLED}
variant="filled"
>
Button
</Action.ActionButton>
@@ -181,7 +177,7 @@ const ActionPage: NextPage = () => {
<Action.ActionButton
block
compact
border
variant="outline"
subtext={
<>
Subtext
@@ -195,8 +191,7 @@ const ActionPage: NextPage = () => {
<Action.ActionButton
block
compact
border
variant={ButtonVariant.FILLED}
variant="filled"
subtext={
<>
Subtext
@@ -210,8 +205,8 @@ const ActionPage: NextPage = () => {
<Action.ActionButton
block
compact
border
size={ButtonSize.SMALL}
variant="outline"
size="small"
subtext={
<>
Subtext
@@ -225,9 +220,8 @@ const ActionPage: NextPage = () => {
<Action.ActionButton
block
compact
border
variant={ButtonVariant.FILLED}
size={ButtonSize.SMALL}
variant="filled"
size="small"
subtext={
<>
Very Long Line of Subtext That Spans More Than The Component Width For Testing Overflow

+ 18
- 0
packages/web-kitchensink-reactnext/src/pages/categories/blob/index.tsx View File

@@ -0,0 +1,18 @@
import {NextPage} from 'next';
import * as React from 'react';
import * as BlobReact from '@tesseract-design/web-blob-react';

const BlobPage: NextPage = () => {
return (
<BlobReact.FileSelectBox
border
enhanced
label="Primary Image"
hint="Select any files here"
block
onChange={(e) => console.log(e.currentTarget.files)}
/>
)
}

export default BlobPage;

packages/web/kitchen-sink/react-next/src/pages/categories/code/index.tsx → packages/web-kitchensink-reactnext/src/pages/categories/code/index.tsx View File

@@ -1,5 +1,5 @@
import { NextPage } from 'next';
import { DefaultLayout } from 'src/components/DefaultLayout';
import { DefaultLayout } from '@/components/DefaultLayout';

const CodePage: NextPage = () => {
return (

+ 617
- 0
packages/web-kitchensink-reactnext/src/pages/categories/freeform/index.tsx View File

@@ -0,0 +1,617 @@
import { NextPage } from 'next';
import * as Freeform from '@tesseract-design/web-freeform-react';
import { DefaultLayout } from '@/components/DefaultLayout';
import { Section, Subsection } from '@/components/Section';

const FreeformPage: NextPage = () => {
return (
<DefaultLayout title="Freeform">
<Section title="TextInput">
<Subsection title="Default">
<div className="grid md:grid-cols-2 gap-4 my-4">
<div>
<Freeform.TextInput
size="small"
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
border
size="small"
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
border
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
size="large"
label="TextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
border
size="large"
label="TextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
label="TextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
<div>
<Freeform.TextInput
border
label="TextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
</div>
<form>
<fieldset className="contents">
<legend className="sr-only">Fieldset Test</legend>
<div className="grid grid-cols-6 gap-4 my-4">
<div className="col-span-2">
<Freeform.TextInput
label="First Name"
block
border
/>
</div>
<div className="col-span-2">
<Freeform.TextInput
label="Middle Name"
block
border
/>
</div>
<div className="col-span-2">
<Freeform.TextInput
label="Last Name"
block
border
/>
</div>
<div className="col-span-3">
<Freeform.TextInput
label="Username"
block
border
/>
</div>
<div className="col-span-3">
<Freeform.TextInput
label="Title"
block
border
disabled
/>
</div>
</div>
</fieldset>
</form>
<form>
<fieldset
disabled
className="contents"
>
<legend className="sr-only">Disabled Test</legend>
<div className="grid grid-cols-6 gap-4 my-4">
<div className="col-span-2">
<Freeform.TextInput
label="First Name"
block
border
/>
</div>
<div className="col-span-2">
<Freeform.TextInput
label="Middle Name"
block
border
/>
</div>
<div className="col-span-2">
<Freeform.TextInput
label="Last Name"
block
border
/>
</div>
<div className="col-span-3">
<Freeform.TextInput
label="Username"
block
border
/>
</div>
<div className="col-span-3">
<Freeform.TextInput
label="Title"
block
border
/>
</div>
</div>
</fieldset>
</form>
<form>
<div>
Hi, my name is
{' '}
<Freeform.TextInput
border
label="Name"
size="small"
/>
{' '}
but you can call me
{' '}
<Freeform.TextInput
border
label="Nickname"
size="small"
/>
.
</div>
</form>
</Subsection>
<Subsection title="Alternate">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Freeform.TextInput
variant="alternate"
size="small"
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
border
variant="alternate"
size="small"
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
variant="alternate"
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
block
/>
</div>
<div>
<Freeform.TextInput
border
variant="alternate"
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
block
/>
</div>
<div>
<Freeform.TextInput
variant="alternate"
size="large"
label="TextInput ffgg"
hint="Type anything here&hellip; ffgg"
indicator="A"
block
/>
</div>
<div>
<Freeform.TextInput
border
block
variant="alternate"
size="large"
label="TextInput"
hint="Type anything here&hellip;"
indicator="A"
/>
</div>
<div>
<Freeform.TextInput
variant="alternate"
label="TextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
<div>
<Freeform.TextInput
variant="alternate"
border
label="TextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
</div>
</Subsection>
</Section>
<Section title="MaskedTextInput">
<Subsection title="Default">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Freeform.MaskedTextInput
size="small"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
border
size="small"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
border
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
size="large"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
border
size="large"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
<div>
<Freeform.MaskedTextInput
border
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
</div>
</Subsection>
<Subsection title="Alternate">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Freeform.MaskedTextInput
variant="alternate"
size="small"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
border
variant="alternate"
size="small"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
variant="alternate"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
border
variant="alternate"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
variant="alternate"
size="large"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MaskedTextInput
border
block
variant="alternate"
size="large"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
/>
</div>
<div>
<Freeform.MaskedTextInput
variant="alternate"
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
<div>
<Freeform.MaskedTextInput
variant="alternate"
border
label="MaskedTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
</div>
</Subsection>
</Section>
<Section title="MultilineTextInput">
<Subsection title="Default">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Freeform.MultilineTextInput
size="small"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
border
size="small"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
border
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
size="large"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
border
size="large"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
<div>
<Freeform.MultilineTextInput
border
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
</div>
</Subsection>
<Subsection title="Alternate">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Freeform.MultilineTextInput
variant="alternate"
size="small"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
border
variant="alternate"
size="small"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
variant="alternate"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
border
variant="alternate"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
variant="alternate"
size="large"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
/>
</div>
<div>
<Freeform.MultilineTextInput
border
block
variant="alternate"
size="large"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
/>
</div>
<div>
<Freeform.MultilineTextInput
variant="alternate"
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
<div>
<Freeform.MultilineTextInput
variant="alternate"
border
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
block
disabled
/>
</div>
</div>
</Subsection>
</Section>
</DefaultLayout>
);
};

export default FreeformPage;

packages/web/kitchen-sink/react-next/src/pages/categories/navigation/index.tsx → packages/web-kitchensink-reactnext/src/pages/categories/navigation/index.tsx View File

@@ -23,15 +23,16 @@ const ActionPage: NextPage = () => {
<div className="grid md:grid-cols-2 gap-4 my-4">
<div>
<Navigation.LinkButton
block
variant="bare"
href="#"
block
>
Button
</Navigation.LinkButton>
</div>
<div>
<Navigation.LinkButton
variant={ButtonVariant.FILLED}
variant="filled"
block
href="#"
>
@@ -40,7 +41,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Navigation.LinkButton
border
variant="outline"
block
href="#"
>
@@ -49,8 +50,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Navigation.LinkButton
border
variant={ButtonVariant.FILLED}
variant="filled"
block
href="#"
>
@@ -62,13 +62,14 @@ const ActionPage: NextPage = () => {
block
disabled
href="#"
variant="bare"
>
Button
</Navigation.LinkButton>
</div>
<div>
<Navigation.LinkButton
variant={ButtonVariant.FILLED}
variant="filled"
block
disabled
href="#"
@@ -78,7 +79,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Navigation.LinkButton
border
variant="outline"
block
disabled
href="#"
@@ -88,8 +89,7 @@ const ActionPage: NextPage = () => {
</div>
<div>
<Navigation.LinkButton
border
variant={ButtonVariant.FILLED}
variant="filled"
block
disabled
href="#"
@@ -107,8 +107,8 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
size={ButtonSize.SMALL}
variant="outline"
size="small"
href="#"
>
Button
@@ -117,9 +117,8 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant={ButtonVariant.FILLED}
size={ButtonSize.SMALL}
variant="filled"
size="small"
href="#"
>
Button
@@ -128,8 +127,8 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
size={ButtonSize.MEDIUM}
variant="outline"
size="medium"
href="#"
>
Button
@@ -138,9 +137,8 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant={ButtonVariant.FILLED}
size={ButtonSize.MEDIUM}
variant="filled"
size="medium"
href="#"
>
Button
@@ -149,8 +147,8 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
size={ButtonSize.LARGE}
variant="outline"
size="large"
href="#"
>
Button
@@ -159,9 +157,8 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant={ButtonVariant.FILLED}
size={ButtonSize.LARGE}
variant="filled"
size="large"
href="#"
>
Button
@@ -178,7 +175,7 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
variant="outline"
href="#"
>
Button
@@ -188,8 +185,7 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
variant={ButtonVariant.FILLED}
variant="filled"
href="#"
>
Button
@@ -198,7 +194,7 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant="outline"
compact
subtext={
<>
@@ -214,8 +210,7 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
variant={ButtonVariant.FILLED}
variant="filled"
subtext={
<>
Subtext
@@ -229,9 +224,9 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant="outline"
compact
size={ButtonSize.SMALL}
size="small"
subtext={
<>
Subtext
@@ -253,9 +248,8 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
variant={ButtonVariant.FILLED}
size={ButtonSize.SMALL}
variant="filled"
size="small"
subtext={
<>
Subtext
@@ -276,9 +270,9 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant="outline"
compact
size={ButtonSize.LARGE}
size="large"
subtext={
<>
Subtext
@@ -300,9 +294,8 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
variant={ButtonVariant.FILLED}
size={ButtonSize.LARGE}
variant="filled"
size="large"
subtext={
<>
Subtext
@@ -323,10 +316,10 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant="outline"
compact
menuItem
size={ButtonSize.SMALL}
size="small"
subtext={
<>
Subtext
@@ -348,9 +341,8 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
size={ButtonSize.SMALL}
variant={ButtonVariant.FILLED}
size="small"
variant="filled"
menuItem
subtext={
<>
@@ -372,7 +364,7 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant="outline"
compact
menuItem
subtext={
@@ -396,8 +388,7 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
variant={ButtonVariant.FILLED}
variant="filled"
menuItem
subtext={
<>
@@ -419,10 +410,10 @@ const ActionPage: NextPage = () => {
<div>
<Navigation.LinkButton
block
border
variant="outline"
compact
menuItem
size={ButtonSize.LARGE}
size="large"
subtext={
<>
Subtext
@@ -444,9 +435,8 @@ const ActionPage: NextPage = () => {
<Navigation.LinkButton
block
compact
border
size={ButtonSize.LARGE}
variant={ButtonVariant.FILLED}
size="large"
variant="filled"
menuItem
subtext={
<>

packages/web/kitchen-sink/react-next/src/pages/categories/number/index.tsx → packages/web-kitchensink-reactnext/src/pages/categories/number/index.tsx View File


packages/web/kitchen-sink/react-next/src/pages/categories/option/index.tsx → packages/web-kitchensink-reactnext/src/pages/categories/option/index.tsx View File

@@ -1,9 +1,9 @@
import { NextPage } from 'next';
import { TextControlSize, TextControlStyle } from '@tesseract-design/web-base-textcontrol';
import { TextControlSize, TextControlVariant } from '@tesseract-design/web-base-textcontrol';
import * as Option from '@tesseract-design/web-option-react';
import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol';
import { ButtonSize, ButtonVariant } from '@tesseract-design/web-base-button';
import { DefaultLayout } from 'src/components/DefaultLayout';
import { DefaultLayout } from '@/components/DefaultLayout';

type Props = {
options: SelectControlBase.SelectOption[],
@@ -216,7 +216,7 @@ const OptionPage: NextPage<Props> = ({
<div className="grid md:grid-cols-2 gap-4">
<div>
<Option.DropdownSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="Select"
hint="Type anything here&hellip;"
@@ -227,7 +227,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.DropdownSelect
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="Select"
hint="Type anything here&hellip;"
@@ -237,7 +237,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.DropdownSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="Select"
hint="Type anything here&hellip;"
block
@@ -247,7 +247,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.DropdownSelect
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="Select"
hint="Type anything here&hellip;"
block
@@ -256,7 +256,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.DropdownSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="Select"
hint="Type anything here&hellip;"
@@ -268,7 +268,7 @@ const OptionPage: NextPage<Props> = ({
<Option.DropdownSelect
border
block
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="Select"
hint="Type anything here&hellip;"
@@ -277,7 +277,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.DropdownSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="Select"
hint="Type anything here&hellip;"
block
@@ -287,7 +287,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.DropdownSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
border
label="Select"
hint="Type anything here&hellip;"
@@ -405,7 +405,7 @@ const OptionPage: NextPage<Props> = ({
<div className="grid md:grid-cols-2 gap-4">
<div>
<Option.MenuSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -417,7 +417,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.MenuSelect
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -428,7 +428,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.MenuSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -439,7 +439,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.MenuSelect
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -449,7 +449,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.MenuSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -462,7 +462,7 @@ const OptionPage: NextPage<Props> = ({
<Option.MenuSelect
border
block
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -472,7 +472,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.MenuSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -483,7 +483,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.MenuSelect
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
border
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -976,7 +976,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.TagInput
enhanced
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="TagInput"
hint="Type anything here&hellip;"
@@ -988,7 +988,7 @@ const OptionPage: NextPage<Props> = ({
<Option.TagInput
enhanced
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="TagInput"
hint="Type anything here&hellip;"
@@ -999,7 +999,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.TagInput
enhanced
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="TagInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1010,7 +1010,7 @@ const OptionPage: NextPage<Props> = ({
<Option.TagInput
enhanced
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="TagInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1020,7 +1020,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.TagInput
enhanced
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="TagInput"
hint="Type anything here&hellip;"
@@ -1033,7 +1033,7 @@ const OptionPage: NextPage<Props> = ({
enhanced
border
block
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="TagInput"
hint="Type anything here&hellip;"
@@ -1043,7 +1043,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.TagInput
enhanced
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="TagInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1054,7 +1054,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.TagInput
enhanced
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
border
label="TagInput"
hint="Type anything here&hellip;"
@@ -1511,7 +1511,7 @@ const OptionPage: NextPage<Props> = ({
<div className="grid md:grid-cols-2 gap-4">
<div>
<Option.ComboBox
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1523,7 +1523,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.ComboBox
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1534,7 +1534,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.ComboBox
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1545,7 +1545,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.ComboBox
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1555,7 +1555,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.ComboBox
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1568,7 +1568,7 @@ const OptionPage: NextPage<Props> = ({
<Option.ComboBox
border
block
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1578,7 +1578,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.ComboBox
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1589,7 +1589,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.ComboBox
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
border
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1700,7 +1700,7 @@ const OptionPage: NextPage<Props> = ({
<div className="grid md:grid-cols-2 gap-4">
<div>
<Option.TagInput
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1711,7 +1711,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.TagInput
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.SMALL}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1721,7 +1721,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.TagInput
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1731,7 +1731,7 @@ const OptionPage: NextPage<Props> = ({
<div>
<Option.TagInput
border
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1740,7 +1740,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.TagInput
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1752,7 +1752,7 @@ const OptionPage: NextPage<Props> = ({
<Option.TagInput
border
block
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
size={TextControlSize.LARGE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
@@ -1761,7 +1761,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.TagInput
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
label="MultilineTextInput"
hint="Type anything here&hellip;"
indicator="A"
@@ -1771,7 +1771,7 @@ const OptionPage: NextPage<Props> = ({
</div>
<div>
<Option.TagInput
style={TextControlStyle.ALTERNATE}
style={TextControlVariant.ALTERNATE}
border
label="MultilineTextInput"
hint="Type anything here&hellip;"

packages/web/kitchen-sink/react-next/src/pages/categories/presentation/index.tsx → packages/web-kitchensink-reactnext/src/pages/categories/presentation/index.tsx View File


packages/web/kitchen-sink/react-next/src/pages/examples/registration-form/index.tsx → packages/web-kitchensink-reactnext/src/pages/examples/registration-form/index.tsx View File

@@ -69,10 +69,9 @@ const RegistrationFormPage: NextPage = () => {
{' '}
<div>
<Action.ActionButton
variant={ButtonVariant.FILLED}
variant="filled"
block
border
type={Action.ActionButtonType.SUBMIT}
type="submit"
menuItem
subtext="Registration requires activation"
>

packages/web/kitchen-sink/react-next/src/pages/index.tsx → packages/web-kitchensink-reactnext/src/pages/index.tsx View File

@@ -21,18 +21,15 @@ const createPageLink = (p: Page) => (
<div
key={p.id}
>
<Link
<Navigation.LinkButton
block
variant="outline"
menuItem
component={Link}
href={p.href}
passHref
>
<Navigation.LinkButton
block
border
menuItem
>
{p.label}
</Navigation.LinkButton>
</Link>
{p.label}
</Navigation.LinkButton>
</div>
)


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

@@ -0,0 +1,41 @@
@tailwind base;
@tailwind utilities;

@layer base {
:root {
--color-shade: 0 0 0;
--color-negative: 34 34 34;
--color-positive: 238 238 238;
--color-primary: 199 138 179;
--color-secondary: 255 153 0;
--color-tertiary: 215 95 75;
--color-code-number: 116 249 94;
--color-code-keyword: 255 67 137;
--color-code-type: 80 151 210;
--color-code-instance-attribute: 118 167 210;
--color-code-function: 103 194 82;
--color-code-parameter: 145 94 194;
--color-code-property: 255 161 201;
--color-code-string: 238 211 113;
--color-code-variable: 139 194 117;
--color-code-regexp: 116 167 43;
--color-code-url: 0 153 204;
--color-code-global: 194 128 80;
}
}

.font-condensed {
font-stretch: condensed;
}

.font-semi-condensed {
font-stretch: semi-condensed;
}

.font-expanded {
font-stretch: expanded;
}

.font-semi-expanded {
font-stretch: semi-expanded;
}

packages/web/kitchen-sink/react-next/public/global.css → packages/web-kitchensink-reactnext/src/styles/kitchen-sink.css View File


packages/web/kitchen-sink/react-next/src/utils/audio.ts → packages/web-kitchensink-reactnext/src/utils/audio.ts View File


packages/web/kitchen-sink/react-next/src/utils/blob.ts → packages/web-kitchensink-reactnext/src/utils/blob.ts View File


packages/web/kitchen-sink/react-next/src/utils/event.ts → packages/web-kitchensink-reactnext/src/utils/event.ts View File


packages/web/kitchen-sink/react-next/src/utils/image.ts → packages/web-kitchensink-reactnext/src/utils/image.ts View File


packages/web/kitchen-sink/react-next/src/utils/numeral.ts → packages/web-kitchensink-reactnext/src/utils/numeral.ts View File


packages/web/kitchen-sink/react-next/src/utils/text.ts → packages/web-kitchensink-reactnext/src/utils/text.ts View File


+ 54
- 0
packages/web-kitchensink-reactnext/tailwind.config.js View File

@@ -0,0 +1,54 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/categories/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
fontFamily: {
sans: ['Encode Sans Semi Expanded', 'Encode Sans', 'sans-serif'],
},
colors: {
'shade': 'rgb(var(--color-shade))',
'negative': 'rgb(var(--color-negative))',
'positive': 'rgb(var(--color-positive))',
'primary': 'rgb(var(--color-primary))',
'secondary': 'rgb(var(--color-secondary))',
'tertiary': 'rgb(var(--color-tertiary))',
'code-number': 'rgb(var(--color-code-number))',
'code-keyword': 'rgb(var(--color-code-keyword))',
'code-type': 'rgb(var(--color-code-type))',
'code-instance-attribute': 'rgb(var(--color-code-instance-attribute))',
'code-function': 'rgb(var(--color-code-function))',
'code-parameter': 'rgb(var(--color-code-parameter))',
'code-property': 'rgb(var(--color-code-property))',
'code-string': 'rgb(var(--color-code-string))',
'code-variable': 'rgb(var(--color-code-variable))',
'code-regexp': 'rgb(var(--color-code-regexp))',
'code-url': 'rgb(var(--color-code-url))',
'code-global': 'rgb(var(--color-code-global))',
'current': 'currentcolor',
},
extend: {
fontSize: {
'xxs': '0.625rem',
},
borderRadius: {
inherit: 'inherit',
},
minWidth: {
6: '1.5rem',
10: '2.5rem',
12: '3rem',
16: '4rem',
},
minHeight: {
10: '2.5rem',
12: '3rem',
16: '4rem',
},
},
},
plugins: [],
}

+ 32
- 0
packages/web-kitchensink-reactnext/tsconfig.json View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./src/*"],
"@tesseract-design/web-base-button": ["./src/base/button"],
"@tesseract-design/web-base-selectcontrol": ["./src/base/selectcontrol"],
"@tesseract-design/web-base-textcontrol": ["./src/base/textcontrol"],
"@tesseract-design/web-action-react": ["./src/categories/action/react"],
"@tesseract-design/web-blob-react": ["./src/categories/blob/react"],
"@tesseract-design/web-freeform-react": ["./src/categories/freeform/react"],
"@tesseract-design/web-information-react": ["./src/categories/information/react"],
"@tesseract-design/web-option-react": ["./src/categories/option/react"],
"@tesseract-design/web-navigation-react": ["./src/categories/navigation/react"],
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

+ 0
- 71
packages/web/base/badge/package.json View File

@@ -1,71 +0,0 @@
{
"name": "@tesseract-design/web-base-badge",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=10"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.2.6",
"csstype": "^3.1.2",
"eslint": "^8.20.0",
"eslint-config-lxsmnsyc": "^0.4.7",
"pridepack": "2.4.4",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vitest": "^0.19.1"
},
"dependencies": {
"@tesseract-design/goofy-goober": "link:../../../../../goofy-goober"
},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Base badge styles for Tesseract.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
},
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js"
}

+ 0
- 3
packages/web/base/badge/pridepack.json View File

@@ -1,3 +0,0 @@
{
"target": "es2017"
}

+ 0
- 45
packages/web/base/badge/src/index.ts View File

@@ -1,45 +0,0 @@
import { css } from '@tesseract-design/goofy-goober';

export type BadgeBaseArgs = {
/**
* Will the component be displayed with circular sides?
*/
rounded: boolean,
}

export const Root = ({
rounded,
}: BadgeBaseArgs) => css.cx(
css`
position: relative;
height: 1.5em;
min-width: 1.5em;
display: inline-grid;
vertical-align: middle;
place-content: center;
overflow: hidden;
font-stretch: var(--font-stretch-base, normal);
padding: 0 0.25rem;
box-sizing: border-box;
&::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: currentColor;
opacity: 0.25;
content: '';
}
`,
css.dynamic({
'border-radius': rounded ? '0.75em' : '0.25rem',
}),
);

export const Content = () => css.cx(
css`
position: relative;
font-size: 0.75em;
`
);

+ 0
- 9
packages/web/base/badge/tsconfig.json View File

@@ -1,9 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"emitDeclarationOnly": true,
"declaration": true
}
}

+ 0
- 3413
packages/web/base/badge/yarn.lock
File diff suppressed because it is too large
View File


+ 0
- 71
packages/web/base/blob/package.json View File

@@ -1,71 +0,0 @@
{
"name": "@tesseract-design/web-base-blob",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=10"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.2.6",
"csstype": "^3.1.2",
"eslint": "^8.20.0",
"eslint-config-lxsmnsyc": "^0.4.7",
"pridepack": "2.4.4",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vitest": "^0.19.1"
},
"dependencies": {
"@tesseract-design/goofy-goober": "link:../../../../../goofy-goober"
},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Base badge styles for Tesseract.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
},
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js"
}

+ 0
- 3
packages/web/base/blob/pridepack.json View File

@@ -1,3 +0,0 @@
{
"target": "es2017"
}

+ 0
- 127
packages/web/base/blob/src/index.ts View File

@@ -1,127 +0,0 @@
import { css } from '@tesseract-design/goofy-goober';

export interface BlobBaseArgs {
border: boolean,
block: boolean,
}

export const Root = ({
block,
}: BlobBaseArgs) => css.cx(
css`
vertical-align: middle;
position: relative;
border-radius: 0.25rem;
font-family: var(--font-family-base, sans-serif);
max-width: 100%;
overflow: hidden;
min-height: 20rem;
min-width: 20rem;
&:focus-within {
--color-accent: var(--color-hover, red);
}
& > span {
border-color: var(--color-accent, blue);
box-sizing: border-box;
display: inline-block;
border-width: 0.125rem;
border-style: solid;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: inherit;
z-index: 2;
pointer-events: none;
transition-property: border-color;
}
& > span::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
border-radius: 0.125rem;
opacity: 0.5;
pointer-events: none;
box-shadow: 0 0 0 0 var(--color-accent, blue);
transition-property: box-shadow;
transition-duration: 150ms;
transition-timing-function: linear;
}
&:focus-within > span::before {
box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
}
`,
css.dynamic({
display: block ? 'block' : 'inline-block',
}),
);

export const Border = ({
border
}: BlobBaseArgs): string => css.cx(
css.if (border) (
css`
border-color: var(--color-accent, blue);
box-sizing: border-box;
display: inline-block;
border-width: 0.125rem;
border-style: solid;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: inherit;
pointer-events: none;
z-index: 10;
&::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
border-radius: 0.125rem;
opacity: 0.5;
pointer-events: none;
}
`
),
);


export const LabelWrapper = ({
border,
}: BlobBaseArgs): string => css.cx(
css`
color: var(--color-accent, blue);
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bolder;
z-index: 1;
pointer-events: none;
transition-property: color;
line-height: 0.65;
user-select: none;
font-size: 0.725em;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
`,
css.if (border) (
css`
background-color: var(--color-background, white);
`
),
)

+ 0
- 9
packages/web/base/blob/tsconfig.json View File

@@ -1,9 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"emitDeclarationOnly": true,
"declaration": true
}
}

+ 0
- 3413
packages/web/base/blob/yarn.lock
File diff suppressed because it is too large
View File


+ 0
- 71
packages/web/base/button/package.json View File

@@ -1,71 +0,0 @@
{
"name": "@tesseract-design/web-base-button",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=10"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.2.6",
"csstype": "^3.1.2",
"eslint": "^8.20.0",
"eslint-config-lxsmnsyc": "^0.4.7",
"pridepack": "2.4.4",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vitest": "^0.19.1"
},
"dependencies": {
"@tesseract-design/goofy-goober": "link:../../../../../goofy-goober"
},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Base button styles for Tesseract.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
},
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js"
}

+ 0
- 3
packages/web/base/button/pridepack.json View File

@@ -1,3 +0,0 @@
{
"target": "es2017"
}

+ 0
- 318
packages/web/base/button/src/index.ts View File

@@ -1,318 +0,0 @@
import { css } from '@tesseract-design/goofy-goober';

export enum ButtonSize {
SMALL = 'small',
MEDIUM = 'medium',
LARGE = 'large',
}

export enum ButtonVariant {
OUTLINE = 'outline',
FILLED = 'filled',
}

export type ButtonBaseArgs = {
/**
* Size of the component.
*/
size: ButtonSize,
/**
* Will the component occupy the whole width of its container?
*/
block: boolean,
/**
* Stylistic variant of the component.
*/
variant: ButtonVariant,
/**
* Will the component display a surrounding border?
*/
border: boolean,
/**
* Will the component reject any activation?
*/
disabled: boolean,
/**
* Will the component conserve visual space?
*/
compact: boolean,
/**
* Is the component an item inside a menu?
*/
menuItem: boolean,
}

const MIN_HEIGHTS: Record<ButtonSize, string> = {
[ButtonSize.SMALL]: '2.5rem',
[ButtonSize.MEDIUM]: '3rem',
[ButtonSize.LARGE]: '4rem',
};

export const Button = ({
size,
block,
variant,
disabled,
compact,
}: ButtonBaseArgs): string => css.cx(
css`
box-sizing: border-box;
vertical-align: middle;
appearance: none;
font: inherit;
font-family: var(--font-family-base, sans-serif);
text-transform: uppercase;
font-weight: bolder;
border-radius: 0.25rem;
justify-content: center;
align-items: center;
position: relative;
border: 0;
user-select: none;
text-decoration: none;
white-space: nowrap;
line-height: 1;

& > :first-child::before {
box-shadow: 0 0 0 0 var(--color-accent, blue);
transition-property: box-shadow;
transition-duration: 150ms;
transition-timing-function: linear;
}

&:disabled {
opacity: 0.5;
cursor: not-allowed;
}

&::-moz-focus-inner {
outline: 0;
border: 0;
}

&:focus > :first-child::before {
box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
}

&:disabled > :first-child::before {
box-shadow: 0 0 0 0 var(--color-accent, blue) !important;
}
`,

css.dynamic({
'min-height': MIN_HEIGHTS[size],
}),

css.if (disabled) (
css`
--color-accent: var(--color-primary, blue);
opacity: 0.5;
cursor: not-allowed;
`
).else (
css`
cursor: pointer;
&:hover {
--color-accent: var(--color-hover, blue);
outline: 0;
}
&:focus {
--color-accent: var(--color-hover, blue);
outline: 0;
}
&:active {
--color-accent: var(--color-active, red);
outline: 0;
}
&:hover > :first-child::before {
box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
}
`
),

css.if (block) (
css`
width: 100%;
display: flex;
`
).else (
css`
display: inline-flex;
`
),

css.if (compact) (
css`
font-stretch: condensed;
padding: 0 0.5rem;
`
).else(
css`
padding: 0 1rem;
`
),

css.if (variant === ButtonVariant.FILLED) (
css`
background-color: var(--color-accent, blue);
color: var(--color-background, white) !important;
`
),

css.if (variant === ButtonVariant.OUTLINE) (
css`
background-color: var(--color-background, white);
color: var(--color-accent, blue);
`
),
);

export const Border = ({
border
}: ButtonBaseArgs): string => css.cx(
css.if (border) (
css`
border-color: var(--color-accent, blue);
box-sizing: border-box;
display: inline-block;
border-width: 0.125rem;
border-style: solid;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: inherit;
pointer-events: none;
&::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
border-radius: 0.125rem;
opacity: 0.5;
pointer-events: none;
}
`
),
);

export const Label = ({
compact,
menuItem,
}: ButtonBaseArgs): string => css.cx(
css`
display: block;
flex-grow: 1;
flex-basis: 0;
min-width: 0;
`,

css.if (compact || menuItem) (
css`
text-align: left;
`
).else (
css`
text-align: center;
`
),

css.if (compact) (
css`
& ~ :last-child {
margin-right: -0.5rem;
}
`
).else (
css`
& ~ :last-child {
margin-right: -1rem;
}
`
),
);

export const BadgeContainer = ({
size,
}: ButtonBaseArgs): string => css.cx(
css`
width: 2rem;
text-align: center;
flex-shrink: 0;
& + * {
margin-left: -0.5rem;
}
`,
css.nest('&:last-child')(
css.dynamic({
width: MIN_HEIGHTS[size],
})
),
);

export const OverflowText = (): string => css.cx(
css`
width: 100%;
display: block;
overflow: hidden;
text-overflow: ellipsis;
height: 1.1em;
line-height: 1;
`,
);

export const IndicatorWrapper = ({
size
}: ButtonBaseArgs): string => css.cx(
css`
flex-shrink: 0;
box-sizing: border-box;
display: grid;
place-content: center;
padding: 0 1rem;
z-index: 1;
pointer-events: none;
line-height: 1;
user-select: none;
`,
css.dynamic({
width: `calc(${MIN_HEIGHTS[size]} * 0.75)`,
height: MIN_HEIGHTS[size],
}),
);

export const Indicator = () => css.cx(
css`
width: 1.5em;
height: 1.5em;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
`,
);

export const MainText = () => css.cx(
css`
width: 100%;
`,
);

export const Subtext = () => css.cx(
css`
display: block;
height: 1.1em;
line-height: 1.1;
width: 100%;
font-size: 0.875em;
text-transform: none;
font-weight: var(--font-weight-base, normal);
`,
);

+ 0
- 9
packages/web/base/button/tsconfig.json View File

@@ -1,9 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"emitDeclarationOnly": true,
"declaration": true
}
}

+ 0
- 3413
packages/web/base/button/yarn.lock
File diff suppressed because it is too large
View File


+ 0
- 71
packages/web/base/checkcontrol/package.json View File

@@ -1,71 +0,0 @@
{
"name": "@tesseract-design/web-base-checkcontrol",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=10"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.2.6",
"csstype": "^3.1.2",
"eslint": "^8.20.0",
"eslint-config-lxsmnsyc": "^0.4.7",
"pridepack": "2.4.4",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vitest": "^0.19.1"
},
"dependencies": {
"@tesseract-design/goofy-goober": "link:../../../../../goofy-goober"
},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Base check control styles for Tesseract.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
},
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js"
}

+ 0
- 3
packages/web/base/checkcontrol/pridepack.json View File

@@ -1,3 +0,0 @@
{
"target": "es2017"
}

+ 0
- 346
packages/web/base/checkcontrol/src/index.ts View File

@@ -1,346 +0,0 @@
import { css } from '@tesseract-design/goofy-goober'

export enum CheckControlAppearance {
TICK_BOX = 'tick-box',
BUTTON = 'button',
SWITCH = 'switch',
}

export enum CheckControlType {
/**
* One or more of this component within its group can be selected.
*/
CHECKBOX = 'checkbox',
/**
* At most one of this component within its group can be selected.
*/
RADIO = 'radio',
}

export type CheckControlBaseArgs = {
/**
* Will the component conserve visual space?
*/
compact: boolean,
/**
* Appearance of the component.
*/
appearance: CheckControlAppearance,
/**
* Will the component occupy the whole width of its container?
*/
block: boolean,
/**
* Type of the component defining its behavior.
*/
type: CheckControlType,
/**
* Label to display signifying the component's unselected state.
*/
uncheckedLabel: boolean,
}

export const CheckStateContainer = ({
appearance,
type,
}: CheckControlBaseArgs): string => css.cx(
css`
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
&:disabled + * {
cursor: not-allowed;
}
&:first-child + * > :first-child + * + * {
align-items: flex-start;
text-align: left;
}
&:checked + * {
--color-accent: var(--color-active, Highlight);
outline: 0;
}
&:indeterminate[type="checkbox"] + * {
--color-accent: var(--color-active, Highlight);
outline: 0;
}
&:indeterminate[type="checkbox"] + * > :first-child + * > * > :first-child {
display: none;
}
&:checked + * > :first-child + * > * > :first-child + * {
display: none;
}
&:focus + * {
--color-accent: var(--color-hover, red);
outline: 0;
}
&:focus[type="checkbox"] + * {
--color-accent: var(--color-hover, red);
outline: 0;
}
&:focus + * > :first-child::before {
box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
}
`,
css.nest('&:checked + * > :first-child + * > *') (
css.if (
appearance === CheckControlAppearance.TICK_BOX
|| appearance === CheckControlAppearance.BUTTON
) (
css.if (type === 'checkbox') (
css`
width: 1.5em;
height: 1.5em;
`
),
css.if (type === 'radio') (
css`
width: 1em;
height: 1em;
`
),
),
css.if (appearance === CheckControlAppearance.SWITCH) (
css`
width: 1em;
height: 1em;
margin-right: 0;
margin-left: 1em;
`
),
),
css.nest('&:first-child + *') (
css`
cursor: pointer;
`,
css.if (appearance === CheckControlAppearance.BUTTON) (
css`
display: flex;
`,
),
css.if (appearance === CheckControlAppearance.SWITCH) (
css`
width: 1em;
height: 1em;
`,
),
),
css.nest('&:indeterminate[type="checkbox"] + * > :first-child + * > *') (
css.if (
appearance === CheckControlAppearance.BUTTON
|| appearance === CheckControlAppearance.TICK_BOX
) (
css`
width: 1.5em;
height: 1.5em;
`
),
css.if (appearance === CheckControlAppearance.SWITCH) (
css`
width: 1em;
height: 1em;
margin-right: 0.5em;
margin-left: 0.5em;
`
)
),
css.nest('&:indeterminate[type="checkbox"] + * > :first-child + * > * > :first-child + *') (
css.if (
appearance === CheckControlAppearance.BUTTON
|| appearance === CheckControlAppearance.TICK_BOX
) (
css`
display: block;
`
),
),
css.nest('&:checked + * > :first-child + * > * > :first-child') (
css.if (
appearance === CheckControlAppearance.BUTTON
|| appearance === CheckControlAppearance.TICK_BOX
) (
css`
display: block;
`
),
),
);

export const ClickArea = (): string => css.cx(
css`
display: contents;
`
);

export const CheckIndicatorArea = ({
compact,
appearance,
type,
uncheckedLabel,
}: CheckControlBaseArgs): string => css.cx(
css`
display: inline-grid;
vertical-align: middle;
place-content: center;
position: relative;
background-color: var(--color-background, white);
box-shadow: 0 0 0 0.125rem var(--color-background, white);
color: var(--color-accent, blue);
overflow: hidden;
&::before {
content: '';
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border-radius: inherit;
border-width: 0.125rem;
border-style: solid;
box-sizing: border-box;
}
`,
css.if (appearance === CheckControlAppearance.TICK_BOX) (
css`
width: 1.5em;
height: 1.5em;
`,
css.if (type === CheckControlType.CHECKBOX) (
css`
border-radius: 0.25rem;
`
),
css.if (type === CheckControlType.RADIO) (
css`
border-radius: 50%;
`
),
),
css.if (appearance === CheckControlAppearance.BUTTON) (
css`
width: 1.5em;
height: 1.5em;
`,
css.if (!compact) (
css`
margin-left: -0.25rem;
`
),
css.if (type === CheckControlType.CHECKBOX) (
css`
border-radius: 0.25rem;
`
),
css.if (type === CheckControlType.RADIO) (
css`
border-radius: 50%;
`
),
),
css.if (appearance === CheckControlAppearance.SWITCH) (
css`
width: 2.5em;
height: 1.5em;
border-radius: 0.75em;
`,
css.if(uncheckedLabel) (
css.dynamic({
'margin-left': compact ? '0.375rem' : '0.75rem',
})
),
),
css.nest('& + *') (
css.dynamic({
'margin-left': compact ? '0.375rem' : '0.75rem',
})
)
);

export const CheckIndicatorWrapper = ({
appearance,
}: CheckControlBaseArgs): string => css.cx(
css`
flex-shrink: 0;
display: grid;
position: relative;
background-color: var(--color-accent, blue);
overflow: hidden;
border-radius: inherit;
`,
css.if(
appearance === CheckControlAppearance.TICK_BOX
|| appearance === CheckControlAppearance.BUTTON
) (
css`
width: 0;
height: 0;
`
),
css.if(appearance === CheckControlAppearance.SWITCH) (
css`
width: 1em;
height: 1em;
margin-right: 1em;
transition-property: margin-left, margin-right;
transition-duration: 150ms;
transition-timing-function: ease-out;
`,
),
);

export const CheckIndicator = ({
appearance,
}: CheckControlBaseArgs) => css.cx(
css`
fill: none;
stroke: var(--color-background, white);
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
width: 1.5em;
height: 1.5em;
`,
css.if(appearance === CheckControlAppearance.SWITCH) (
css`
display: none;
`
)
);

export const ClickAreaWrapper = ({
block,
appearance,
uncheckedLabel,
}: CheckControlBaseArgs) => css.cx(
css`
vertical-align: middle;
`,
css.dynamic({
display: block ? 'block' : 'inline-block',
}),
css.if (appearance === CheckControlAppearance.TICK_BOX) (
css`
padding-left: 2.25rem;
text-indent: -2.25rem;
`
),
css.if (appearance === CheckControlAppearance.SWITCH) (
css.if (!uncheckedLabel) (
css`
padding-left: 3.25rem;
text-indent: -3.25rem;
`
),
),
);

export const Subtext = () => css.cx(
css`
font-size: 0.875em;
`
);

+ 0
- 9
packages/web/base/checkcontrol/tsconfig.json View File

@@ -1,9 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"emitDeclarationOnly": true,
"declaration": true
}
}

+ 0
- 3413
packages/web/base/checkcontrol/yarn.lock
File diff suppressed because it is too large
View File


+ 0
- 67
packages/web/base/selectcontrol/package.json View File

@@ -1,67 +0,0 @@
{
"name": "@tesseract-design/web-base-selectcontrol",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=10"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"devDependencies": {
"@types/node": "^18.0.0",
"eslint": "^8.20.0",
"eslint-config-lxsmnsyc": "^0.4.7",
"pridepack": "2.4.4",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vitest": "^0.19.1"
},
"dependencies": {},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Base select control styles for Tesseract.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
},
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js"
}

+ 0
- 3
packages/web/base/selectcontrol/pridepack.json View File

@@ -1,3 +0,0 @@
{
"target": "es2017"
}

+ 0
- 5
packages/web/base/selectcontrol/src/index.ts View File

@@ -1,5 +0,0 @@
export interface SelectOption {
label: string,
value?: string | number | readonly string[]
children?: SelectOption[]
}

+ 0
- 9
packages/web/base/selectcontrol/tsconfig.json View File

@@ -1,9 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"emitDeclarationOnly": true,
"declaration": true
}
}

+ 0
- 3380
packages/web/base/selectcontrol/yarn.lock
File diff suppressed because it is too large
View File


+ 0
- 71
packages/web/base/textcontrol/package.json View File

@@ -1,71 +0,0 @@
{
"name": "@tesseract-design/web-base-textcontrol",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=10"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.2.6",
"csstype": "^3.1.2",
"eslint": "^8.20.0",
"eslint-config-lxsmnsyc": "^0.4.7",
"pridepack": "2.4.4",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vitest": "^0.19.1"
},
"dependencies": {
"@tesseract-design/goofy-goober": "link:../../../../../goofy-goober"
},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Base text control styles for Tesseract.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
},
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js"
}

+ 0
- 3
packages/web/base/textcontrol/pridepack.json View File

@@ -1,3 +0,0 @@
{
"target": "es2017"
}

+ 0
- 405
packages/web/base/textcontrol/src/index.ts View File

@@ -1,405 +0,0 @@
import { css } from '@tesseract-design/goofy-goober';

export enum TextControlSize {
SMALL = 'small',
MEDIUM = 'medium',
LARGE = 'large',
}

export enum TextControlStyle {
DEFAULT = 'default',
ALTERNATE = 'alternate',
}

export enum TextControlInputType {
TEXT = 'text',
SEARCH = 'search',
}

export const MIN_HEIGHTS: Record<TextControlSize, string> = {
[TextControlSize.SMALL]: '2.5rem',
[TextControlSize.MEDIUM]: '3rem',
[TextControlSize.LARGE]: '4rem',
};

const LABEL_VERTICAL_PADDING_SIZES: Record<TextControlSize, string> = {
[TextControlSize.SMALL]: '0.125rem',
[TextControlSize.MEDIUM]: '0.25rem',
[TextControlSize.LARGE]: '0.375rem',
};

const INPUT_FONT_SIZES: Record<TextControlSize, string> = {
[TextControlSize.SMALL]: '0.75em',
[TextControlSize.MEDIUM]: '0.85em',
[TextControlSize.LARGE]: '1em',
};

const SECONDARY_TEXT_SIZES: Record<TextControlSize, string> = {
[TextControlSize.SMALL]: '0.6em',
[TextControlSize.MEDIUM]: '0.725em',
[TextControlSize.LARGE]: '0.85em',
};

const MULTILINE_VERTICAL_PADDING_FACTORS: Record<TextControlSize, string> = {
[TextControlSize.SMALL]: '1.25',
[TextControlSize.MEDIUM]: '1.2',
[TextControlSize.LARGE]: '1.45',
};

const ALTERNATE_VERTICAL_PADDING_FACTORS: Record<TextControlSize, string> = {
[TextControlSize.SMALL]: '1.75',
[TextControlSize.MEDIUM]: '1.35',
[TextControlSize.LARGE]: '1.25',
};

export type TextControlBaseArgs = {
/**
* Will the component occupy the whole width of its container?
*/
block: boolean,
/**
* Stylistic variant of the component.
*/
style: TextControlStyle,
/**
* Will the component display a surrounding border?
*/
border: boolean,
/**
* Does the component include an additional indicator for labels?
*/
indicator: boolean,
/**
* Size of the component.
*/
size: TextControlSize,
/**
* Can the size of the component be changed?
*/
resizable: boolean,
/**
* Does this component have predefined values?
*/
predefinedValues: boolean,
}

export const Root = ({
block,
}: TextControlBaseArgs): string => css.cx(
css`
vertical-align: middle;
position: relative;
border-radius: 0.25rem;
font-family: var(--font-family-base, sans-serif);
max-width: 100%;
&:focus-within {
--color-accent: var(--color-hover, red);
}
& > span {
border-color: var(--color-accent, blue);
box-sizing: border-box;
display: inline-block;
border-width: 0.125rem;
border-style: solid;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: inherit;
z-index: 2;
pointer-events: none;
transition-property: border-color;
}
& > span::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
border-radius: 0.125rem;
opacity: 0.5;
pointer-events: none;
box-shadow: 0 0 0 0 var(--color-accent, blue);
transition-property: box-shadow;
transition-duration: 150ms;
transition-timing-function: linear;
}
&:focus-within > span::before {
box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
}
`,
css.dynamic({
display: block ? 'block' : 'inline-block',
}),
);

export const LabelWrapper = ({
style,
border,
indicator,
size,
}: TextControlBaseArgs): string => css.cx(
css`
color: var(--color-accent, blue);
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bolder;
z-index: 1;
pointer-events: none;
transition-property: color;
line-height: 0.65;
user-select: none;
`,
css.dynamic({
'padding-bottom': LABEL_VERTICAL_PADDING_SIZES[size],
'font-size': SECONDARY_TEXT_SIZES[size],
}),
css.if (border) (
css`
background-color: var(--color-background, white);
`
),
css.if (style === TextControlStyle.ALTERNATE) (
css.dynamic({
'padding-top': `calc(${LABEL_VERTICAL_PADDING_SIZES[size]} * 0.5)`,
}),
css.if (border) (
css`
padding-left: 0.5rem;
`,
css.dynamic({
'padding-right': indicator ? MIN_HEIGHTS[size] : '0.5rem',
}),
),
css.if (!border && indicator) (
css.dynamic({
'padding-right': MIN_HEIGHTS[size],
}),
),
),
css.if (style === TextControlStyle.DEFAULT) (
css`
padding-left: 0.5rem;
`,
css.dynamic({
'padding-top': LABEL_VERTICAL_PADDING_SIZES[size],
'padding-right': !indicator ? '0.5rem' : MIN_HEIGHTS[size],
}),
),
)

export const Input = ({
style,
size,
indicator,
border,
resizable,
predefinedValues,
}: TextControlBaseArgs): string => css.cx(
css`
appearance: none;
display: block;
box-sizing: border-box;
position: relative;
border: 0;
border-radius: inherit;
margin: 0;
font-family: inherit;
min-width: 8rem;
max-width: 100%;
width: 100%;
z-index: 1;
transition-property: background-color, color;
&:focus {
outline: 0;
color: var(--color-foreground, black);
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
&:disabled ~ * {
opacity: 0.5;
}
`,
css.media('only screen') (
css`
background-color: var(--color-background, white);
color: var(--color-foreground, black);
`
),
css.dynamic({
'min-height': MIN_HEIGHTS[size],
'font-size': INPUT_FONT_SIZES[size],
}),
css.if (resizable) (
css`
resize: vertical;
`
),
css.if (predefinedValues) (
css`
cursor: pointer;
`
),
css.if (border) (
css`
background-color: var(--color-background, white);
`
),
css.if (style === TextControlStyle.ALTERNATE) (
css`
padding-bottom: 0;
`,
css.dynamic({
'padding-top': resizable
? `calc(${SECONDARY_TEXT_SIZES[size]} * 2.5)`
: `calc(${SECONDARY_TEXT_SIZES[size]} * 2)`,
'line-height': `calc(${MULTILINE_VERTICAL_PADDING_FACTORS[size]} * 1.1)`,
}),
css.if (border) (
css`
padding-left: 0.5rem;
`,
css.dynamic({
'padding-right': indicator ? MIN_HEIGHTS[size] : '0.5rem',
}),
),
css.if (!border && indicator) (
css.dynamic({
'padding-right': MIN_HEIGHTS[size],
}),
)
),
css.if (style === TextControlStyle.DEFAULT) (
css`
padding-left: 1rem;
`,
css.dynamic({
'padding-right': !indicator ? '1rem' : MIN_HEIGHTS[size],
'line-height': `calc(${MULTILINE_VERTICAL_PADDING_FACTORS[size]} * 1.1)`,
}),
css.if (resizable) (
css.dynamic({
'padding-top': `calc(${SECONDARY_TEXT_SIZES[size]} * ${MULTILINE_VERTICAL_PADDING_FACTORS[size]})`,
'padding-bottom': `calc(${SECONDARY_TEXT_SIZES[size]} * ${MULTILINE_VERTICAL_PADDING_FACTORS[size]})`,
})
),
css.if (!resizable) (
css.dynamic({
'padding-bottom': `calc(${SECONDARY_TEXT_SIZES[size]} * ${MULTILINE_VERTICAL_PADDING_FACTORS[size]} * 0.5)`,
})
)
),
)

export const HintWrapper = ({
style,
size,
border,
}: TextControlBaseArgs): string => css.cx(
css`
box-sizing: border-box;
position: absolute;
left: 0;
font-size: 0.85em;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
z-index: 1;
pointer-events: none;
user-select: none;
line-height: 0;
`,
css.if (border) (
css`
background-color: var(--color-background, white);
`
),
css.if (style === TextControlStyle.ALTERNATE) (
css`
line-height: 1.25;
`,
css.dynamic({
top: `calc(${SECONDARY_TEXT_SIZES[size]} * ${ALTERNATE_VERTICAL_PADDING_FACTORS[size]})`,
'font-size': SECONDARY_TEXT_SIZES[size],
}),
css.if (border) (
css`
padding-left: 0.5rem;
&:last-child {
padding-right: 0.5rem;
}
`,
css.dynamic({
'padding-right': MIN_HEIGHTS[size],
})
)
),
css.if (style === TextControlStyle.DEFAULT) (
css`
bottom: 0;
padding-left: 1rem;
line-height: 1.25;
&:last-child {
padding-right: 1rem;
}
`,
css.dynamic({
'padding-bottom': `calc(${LABEL_VERTICAL_PADDING_SIZES[size]} * 0.9)`,
'padding-right': MIN_HEIGHTS[size],
'font-size': SECONDARY_TEXT_SIZES[size],
})
)
)

export const Hint = (): string => css.cx(
css`
opacity: 0.5;
`
);

export const IndicatorWrapper = ({
size
}: TextControlBaseArgs): string => css.cx(
css`
color: var(--color-accent, blue);
box-sizing: border-box;
position: absolute;
bottom: 0;
right: 0;
display: grid;
place-content: center;
padding: 0 1rem;
z-index: 2;
pointer-events: none;
transition-property: color;
line-height: 1;
user-select: none;
`,
css.dynamic({
width: MIN_HEIGHTS[size],
height: MIN_HEIGHTS[size],
}),
);

export const Indicator = (): string => css.cx(
css`
width: 1.5em;
height: 1.5em;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
`,
);

+ 0
- 9
packages/web/base/textcontrol/tsconfig.json View File

@@ -1,9 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"emitDeclarationOnly": true,
"declaration": true
}
}

+ 0
- 3413
packages/web/base/textcontrol/yarn.lock
File diff suppressed because it is too large
View File


+ 0
- 82
packages/web/categories/action/react/package.json View File

@@ -1,82 +0,0 @@
{
"name": "@tesseract-design/web-action-react",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=10"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"dependencies": {
"@tesseract-design/web-base-button": "link:../../../base/button"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^13.5.0",
"@types/node": "^18.0.0",
"@types/react": "^18.0.14",
"eslint": "^8.20.0",
"eslint-config-lxsmnsyc": "^0.4.7",
"jsdom": "^20.0.0",
"pridepack": "2.4.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-test-renderer": "^18.2.0",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vitest": "^0.31.0"
},
"peerDependencies": {
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Action components for Tesseract for use in React.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
},
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js"
}

+ 0
- 3
packages/web/categories/action/react/pridepack.json View File

@@ -1,3 +0,0 @@
{
"target": "es2017"
}

+ 0
- 4
packages/web/categories/action/react/setupTests.ts View File

@@ -1,4 +0,0 @@
import matchers from '@testing-library/jest-dom/matchers';
import '@testing-library/jest-dom';

expect.extend(matchers);

+ 0
- 190
packages/web/categories/action/react/src/components/ActionButton/ActionButton.test.tsx View File

@@ -1,190 +0,0 @@
import * as React from 'react';
import {
render,
screen,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as ButtonBase from '@tesseract-design/web-base-button';
import { vi } from 'vitest';
import {
ActionButton,
ActionButtonType,
} from '.';

vi.mock('@tesseract-design/web-base-button');

describe('ActionButton', () => {
it('renders a button', () => {
render(
<ActionButton />
);
const button: HTMLButtonElement = screen.getByRole('button');
expect(button).toBeInTheDocument();
expect(button).toHaveProperty('type', 'button');
});

it('renders a subtext', () => {
render(
<ActionButton
subtext="subtext"
/>
);
const subtext: HTMLElement = screen.getByTestId('subtext');
expect(subtext).toBeInTheDocument();
});

it('renders a badge', () => {
render(
<ActionButton
badge="badge"
/>
);
const badge: HTMLElement = screen.getByTestId('badge');
expect(badge).toBeInTheDocument();
});

it('renders as a menu item', () => {
render(
<ActionButton
menuItem
/>
);
const menuItemIndicator: HTMLElement = screen.getByTestId('menuItemIndicator');
expect(menuItemIndicator).toBeInTheDocument();
});

it('handles click events', () => {
const onClick = vi.fn().mockImplementationOnce((e) => { e.preventDefault() });
render(
<ActionButton
onClick={onClick}
/>
);
const button: HTMLButtonElement = screen.getByRole('button');
userEvent.click(button);
expect(onClick).toBeCalled();
});

it('renders a compact button', () => {
render(
<ActionButton
compact
/>
);

expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({
compact: true,
}));

expect(ButtonBase.Label).toBeCalledWith(expect.objectContaining({
compact: true,
}));
});

describe.each(Object.values(ButtonBase.ButtonSize))('on %s size', (size) => {
it('renders button styles', () => {
render(
<ActionButton
size={size}
/>
);

expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({
size,
}));
});

it('renders badge styles', () => {
render(
<ActionButton
size={size}
badge="badge"
/>
);

expect(ButtonBase.BadgeContainer).toBeCalledWith(expect.objectContaining({
size,
}));
});

it('renders indicator styles', () => {
render(
<ActionButton
size={size}
menuItem
/>
);

expect(ButtonBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({
size,
}));
});
});

it.each(Object.values(ButtonBase.ButtonVariant))('renders a button with variant %s', (variant) => {
render(
<ActionButton
variant={variant}
/>
);

expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({
variant,
}));
});

it('renders a bordered button', () => {
render(
<ActionButton
border
/>
);

expect(ButtonBase.Border).toBeCalledWith(expect.objectContaining({
border: true,
}));
});

it('renders a block button', () => {
render(
<ActionButton
block
/>
);

expect(ButtonBase.Border).toBeCalledWith(expect.objectContaining({
block: true,
}));
});

it('renders children', () => {
render(
<ActionButton>
Foo
</ActionButton>
);

const children: HTMLElement = screen.getByTestId('children');
expect(children).toHaveTextContent('Foo');
});

it.each(Object.values(ActionButtonType))('renders a button with type %s', (buttonType) => {
render(
<ActionButton
type={buttonType}
/>
);
const button: HTMLButtonElement = screen.getByRole('button');
expect(button).toHaveProperty('type', buttonType);
});

it('renders a disabled button', () => {
render(
<ActionButton
disabled
/>
);
const button: HTMLButtonElement = screen.getByRole('button');
expect(button).toBeDisabled();
});
});

+ 0
- 172
packages/web/categories/action/react/src/components/ActionButton/index.tsx View File

@@ -1,172 +0,0 @@
import * as React from 'react';
import * as ButtonBase from '@tesseract-design/web-base-button';

/**
* Available ActionButton type values.
*/
export enum ActionButtonType {
SUBMIT = 'submit',
RESET = 'reset',
BUTTON = 'button',
}

/**
* Props for the component.
*/
export interface ActionButtonProps extends Omit<React.HTMLProps<HTMLButtonElement>, 'size' | 'type' | 'style'> {
/**
* Size of the component.
*/
size?: ButtonBase.ButtonSize,
/**
* Variant of the component.
*/
variant?: ButtonBase.ButtonVariant,
/**
* Should the component display a border?
*/
border?: boolean,
/**
* Should the component occupy the whole width of its parent?
*/
block?: boolean,
/**
* Type of the component.
*/
type?: ActionButtonType,
/**
* Does the component need to conserve space?
*/
compact?: boolean,
/**
* Complementary content of the component.
*/
subtext?: React.ReactNode,
/**
* Short complementary content displayed at the edge of the component.
*/
badge?: React.ReactNode,
/**
* Is this component part of a menu?
*/
menuItem?: boolean,
}

/**
* Component for performing an action upon activation (e.g. when clicked).
*
* This component functions as a regular button.
*/
export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProps>(
(
{
size = ButtonBase.ButtonSize.MEDIUM,
variant = ButtonBase.ButtonVariant.OUTLINE,
border = false,
children,
type = ActionButtonType.BUTTON,
block = false,
disabled = false,
compact = false,
subtext,
badge,
menuItem = false,
className: _className,
as: _as,
...etcProps
}: ActionButtonProps,
ref,
) => {
const styleProps: ButtonBase.ButtonBaseArgs = {
size,
block,
variant,
border,
compact,
menuItem,
disabled,
};

return (
<button
{...etcProps}
disabled={disabled}
className={ButtonBase.Button(styleProps)}
ref={ref}
type={type}
>
<span
className={ButtonBase.Border(styleProps)}
/>
<span
className={ButtonBase.Label(styleProps)}
>
<span
className={ButtonBase.MainText()}
data-testid="children"
>
<span
className={ButtonBase.OverflowText()}
>
{children}
</span>
</span>
{
subtext
&& (
<>
{' '}
<span
className={ButtonBase.Subtext()}
data-testid="subtext"
>
<span
className={ButtonBase.OverflowText()}
>
{subtext}
</span>
</span>
</>
)
}
</span>
{
badge
&& (
<>
{' '}
<span
className={ButtonBase.BadgeContainer(styleProps)}
data-testid="badge"
>
{badge}
</span>
</>
)
}
{
menuItem
&& (
<>
{' '}
<span
className={ButtonBase.IndicatorWrapper(styleProps)}
data-testid="menuItemIndicator"
>
<svg
className={ButtonBase.Indicator()}
viewBox="0 0 24 24"
role="presentation"
>
<polyline points="9 18 15 12 9 6"/>
</svg>
</span>
</>
)
}
</button>
);
},
);

ActionButton.displayName = 'ActionButton';

+ 0
- 9
packages/web/categories/action/react/src/web-action-react.test.ts View File

@@ -1,9 +0,0 @@
import * as WebActionReact from '.';

describe('web-action-react', () => {
it.each([
'ActionButton',
])('exports %s', (namedExport) => {
expect(WebActionReact).toHaveProperty(namedExport);
});
});

+ 0
- 11
packages/web/categories/action/react/tsconfig.json View File

@@ -1,11 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "../../../../../tsconfig.json",
"compilerOptions": {
"lib": ["dom"],
"rootDir": "src",
"jsx": "react",
"emitDeclarationOnly": true,
"declaration": true
}
}

+ 0
- 9
packages/web/categories/action/react/vite.config.ts View File

@@ -1,9 +0,0 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['setupTests.ts'],
},
})

+ 0
- 4209
packages/web/categories/action/react/yarn.lock
File diff suppressed because it is too large
View File


+ 0
- 81
packages/web/categories/blob/react/package.json View File

@@ -1,81 +0,0 @@
{
"name": "@tesseract-design/web-blob-react",
"version": "0.0.0",
"files": [
"dist",
"src"
],
"engines": {
"node": ">=12"
},
"license": "MIT",
"keywords": [
"pridepack"
],
"dependencies": {
"@tesseract-design/web-base-blob": "link:../../../base/blob",
"@tesseract-design/web-base-button": "link:../../../base/button"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@types/node": "^18.14.1",
"@types/react": "^18.0.27",
"eslint": "^8.35.0",
"eslint-config-lxsmnsyc": "^0.5.0",
"jsdom": "^21.1.0",
"pridepack": "2.4.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-test-renderer": "^18.2.0",
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vitest": "^0.28.1"
},
"peerDependencies": {
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"scripts": {
"prepublishOnly": "pridepack clean && pridepack build",
"build": "pridepack build",
"type-check": "pridepack check",
"lint": "pridepack lint",
"clean": "pridepack clean",
"watch": "pridepack watch",
"start": "pridepack start",
"dev": "pridepack dev",
"test": "vitest"
},
"private": true,
"description": "Blob components for Tesseract for use in React.",
"repository": {
"url": "",
"type": "git"
},
"homepage": "",
"bugs": {
"url": ""
},
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>",
"publishConfig": {
"access": "restricted"
},
"types": "./dist/types/index.d.ts",
"main": "./dist/cjs/production/index.js",
"module": "./dist/esm/production/index.js",
"exports": {
".": {
"development": {
"require": "./dist/cjs/development/index.js",
"import": "./dist/esm/development/index.js"
},
"require": "./dist/cjs/production/index.js",
"import": "./dist/esm/production/index.js",
"types": "./dist/types/index.d.ts"
}
},
"typesVersions": {
"*": {}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save