@@ -0,0 +1,3 @@ | |||||
{ | |||||
"extends": "next/core-web-vitals" | |||||
} |
@@ -0,0 +1,37 @@ | |||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | |||||
# dependencies | |||||
/node_modules | |||||
/.pnp | |||||
.pnp.js | |||||
# testing | |||||
/coverage | |||||
# next.js | |||||
/.next/ | |||||
/out/ | |||||
# production | |||||
/build | |||||
# misc | |||||
.DS_Store | |||||
*.pem | |||||
# debug | |||||
npm-debug.log* | |||||
yarn-debug.log* | |||||
yarn-error.log* | |||||
# local env files | |||||
.env | |||||
.env.local | |||||
.env.development.local | |||||
.env.test.local | |||||
.env.production.local | |||||
# vercel | |||||
.vercel | |||||
.idea/ |
@@ -0,0 +1 @@ | |||||
@tesseract-design:registry=http://localhost:4873/ |
@@ -0,0 +1,34 @@ | |||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). | |||||
## Getting Started | |||||
First, run the development server: | |||||
```bash | |||||
npm run dev | |||||
# or | |||||
yarn dev | |||||
``` | |||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. | |||||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. | |||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. | |||||
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. | |||||
## Learn More | |||||
To learn more about Next.js, take a look at the following resources: | |||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. | |||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. | |||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! | |||||
## Deploy on Vercel | |||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. | |||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. |
@@ -0,0 +1,10 @@ | |||||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ | |||||
module.exports = { | |||||
preset: 'ts-jest', | |||||
testEnvironment: 'jsdom', | |||||
globals: { | |||||
'ts-jest': { | |||||
tsconfig: 'tsconfig.test.json', | |||||
}, | |||||
}, | |||||
}; |
@@ -0,0 +1,6 @@ | |||||
/// <reference types="next" /> | |||||
/// <reference types="next/types/global" /> | |||||
/// <reference types="next/image-types/global" /> | |||||
// NOTE: This file should not be edited | |||||
// see https://nextjs.org/docs/basic-features/typescript for more information. |
@@ -0,0 +1,4 @@ | |||||
/** @type {import('next').NextConfig} */ | |||||
module.exports = { | |||||
reactStrictMode: true, | |||||
} |
@@ -0,0 +1,27 @@ | |||||
{ | |||||
"name": "@tesseract-design/web-kitchen-sink", | |||||
"version": "0.1.0", | |||||
"private": true, | |||||
"scripts": { | |||||
"dev": "next dev", | |||||
"build": "next build", | |||||
"start": "next start", | |||||
"lint": "next lint" | |||||
}, | |||||
"dependencies": { | |||||
"goober": "^2.0.41", | |||||
"next": "11.1.2", | |||||
"react": "17.0.2", | |||||
"react-dom": "17.0.2", | |||||
"tailwindcss": "^2.2.16" | |||||
}, | |||||
"devDependencies": { | |||||
"@types/jest": "^27.0.3", | |||||
"@types/react": "17.0.27", | |||||
"eslint": "^7.32.0", | |||||
"eslint-config-next": "11.1.2", | |||||
"jest": "^27.4.3", | |||||
"ts-jest": "^27.0.7", | |||||
"typescript": "4.4.3" | |||||
} | |||||
} |
@@ -0,0 +1,93 @@ | |||||
body { | |||||
margin: 0; | |||||
} | |||||
title { | |||||
font-size: 4em; | |||||
text-transform: lowercase; | |||||
} | |||||
meta[name="display-title"] { | |||||
font-size: 4em; | |||||
text-transform: lowercase; | |||||
} | |||||
meta[name="display-title"]::before { | |||||
display: block; | |||||
content: attr(content); | |||||
} | |||||
h1 { | |||||
font-size: 3em; | |||||
text-transform: lowercase; | |||||
} | |||||
h2 { | |||||
font-size: 2em; | |||||
text-transform: lowercase; | |||||
} | |||||
h3 { | |||||
font-size: 1.75em; | |||||
text-transform: lowercase; | |||||
} | |||||
h4 { | |||||
font-size: 1.5em; | |||||
text-transform: lowercase; | |||||
} | |||||
h5 { | |||||
font-size: 1.25em; | |||||
text-transform: lowercase; | |||||
} | |||||
h6 { | |||||
font-size: 1.125em; | |||||
text-transform: lowercase; | |||||
} | |||||
p { | |||||
margin: 2em 0; | |||||
} | |||||
small { | |||||
font-size: 0.75em; | |||||
} | |||||
a:focus { | |||||
outline: 0; | |||||
} | |||||
pre { | |||||
overflow: auto; | |||||
margin: 0 -1rem; | |||||
padding: 0 1rem; | |||||
line-height: 1.2; | |||||
box-sizing: border-box; | |||||
} | |||||
@media only print { | |||||
pre, pre * { | |||||
color: inherit !important; | |||||
} | |||||
code, code * { | |||||
color: inherit !important; | |||||
} | |||||
img { | |||||
filter: grayscale(100%); | |||||
} | |||||
:root { | |||||
--color-accent: black !important; | |||||
--color-active: black !important; | |||||
} | |||||
} | |||||
/* kitchen-sink styles */ | |||||
.container { | |||||
max-width: 720px; | |||||
} |
@@ -0,0 +1,181 @@ | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 400; | |||||
font-style: normal; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-Regular.woff2) format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 700; | |||||
font-style: normal; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki Bold'), | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-Bold.woff2) format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 400; | |||||
font-style: italic; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki Italic'), | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-Italic.woff2) format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'mononoki'; | |||||
font-weight: 700; | |||||
font-style: italic; | |||||
font-display: swap; | |||||
src: | |||||
local('mononoki Bold Italic'), | |||||
local('mononoki BoldItalic'), | |||||
local('mononoki'), | |||||
url(fonts/mononoki/mononoki-BoldItalic.woff2) format('woff2'); | |||||
} | |||||
:root { | |||||
--font-family-base: 'Encode Sans', system-ui; | |||||
--font-stretch-base: semi-expanded; | |||||
--font-weight-base: 400; | |||||
--line-height-base: 1.5em; | |||||
--font-family-headings: Glory, 'Encode Sans', system-ui; | |||||
--font-stretch-headings: condensed; | |||||
--font-weight-headings: 100; | |||||
--line-height-headings: 1.125em; | |||||
--font-family-monospace: 'mononoki'; | |||||
--font-size-root: 16px; | |||||
--opacity-light: 0.25; | |||||
--opacity-lighter: 0.5; | |||||
--opacity-lightest: 0.75; | |||||
} | |||||
:root { | |||||
--color-bg: var(--color-negative, white); | |||||
--color-fg: var(--color-positive, black); | |||||
--color-accent: var(--color-primary, blue); | |||||
--color-active: var(--color-tertiary, red); | |||||
--color-hover: var(--color-secondary, blue); | |||||
} | |||||
@media (prefers-color-scheme: dark) { | |||||
:root { | |||||
--color-bg: var(--color-negative, black); | |||||
--color-fg: var(--color-positive, white); | |||||
} | |||||
} | |||||
:root { | |||||
font-size: var(--font-size-root); | |||||
font-family: var(--font-family-base), sans-serif; | |||||
font-stretch: var(--font-stretch-base, normal); | |||||
font-weight: var(--font-weight-base, 400); | |||||
line-height: var(--line-height-base, 1.5); | |||||
transition-property: color, background-color; | |||||
transition-timing-function: ease; | |||||
transition-duration: 350ms; | |||||
} | |||||
@media only screen { | |||||
:root { | |||||
background-color: var(--color-bg); | |||||
color: var(--color-fg); | |||||
} | |||||
} | |||||
title { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
meta[name="display-title"] { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h1 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h2 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: calc(var(--font-weight-headings, 400) / 100 * 150); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h3 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: calc(var(--font-weight-headings, 400) / 100 * 250); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h4 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h5 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
h6 { | |||||
font-family: var(--font-family-headings), sans-serif; | |||||
font-stretch: var(--font-stretch-headings, normal); | |||||
font-weight: var(--font-weight-headings, 400); | |||||
line-height: var(--line-height-headings, 1.5); | |||||
} | |||||
code { | |||||
font-family: var(--font-family-monospace), monospace; | |||||
} | |||||
pre { | |||||
font-family: var(--font-family-monospace), monospace; | |||||
} | |||||
a { | |||||
color: inherit; | |||||
text-decoration: none; | |||||
} | |||||
@media only screen { | |||||
a { | |||||
color: var(--color-accent); | |||||
text-decoration: underline; | |||||
} | |||||
a:focus { | |||||
color: var(--color-active); | |||||
} | |||||
} | |||||
::selection { | |||||
background-color: var(--color-active); | |||||
color: var(--color-fg); | |||||
} | |||||
:root { | |||||
caret-color: var(--color-active); | |||||
} |
@@ -0,0 +1,20 @@ | |||||
:root { | |||||
--color-shade: #000; | |||||
--color-negative: #222; | |||||
--color-positive: #eee; | |||||
--color-primary: #C78AB3; | |||||
--color-secondary: #f90; | |||||
--color-tertiary: #d75f4b; | |||||
--color-code-number: #74f95e; | |||||
--color-code-keyword: #ff4389; | |||||
--color-code-type: #5097D2; | |||||
--color-code-instance-attribute: #76a7d2; | |||||
--color-code-function: #67c252; | |||||
--color-code-parameter: #915ec2; | |||||
--color-code-property: #ffa1c9; | |||||
--color-code-string: #eed371; | |||||
--color-code-variable: #8bc275; | |||||
--color-code-regexp: #74A72B; | |||||
--color-code-url: #0099CC; | |||||
--color-code-global: #C28050; | |||||
} |
@@ -0,0 +1,20 @@ | |||||
:root { | |||||
--color-shade: #fff; | |||||
--color-negative: #f8f8f8; | |||||
--color-positive: #333; | |||||
--color-primary: #ba6a9c; | |||||
--color-secondary: #f90; | |||||
--color-tertiary: #d75f4b; | |||||
--color-code-number: #72b507; | |||||
--color-code-keyword: #ee5189; | |||||
--color-code-type: #427fb1; | |||||
--color-code-instance-attribute: #76a7d2; | |||||
--color-code-function: #5a984a; | |||||
--color-code-parameter: #915ec2; | |||||
--color-code-property: #b76e8d; | |||||
--color-code-string: #b59e36; | |||||
--color-code-variable: #61864e; | |||||
--color-code-regexp: #4f7e03; | |||||
--color-code-url: #0099CC; | |||||
--color-code-global: #C28050; | |||||
} |
@@ -0,0 +1,4 @@ | |||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none" | |||||
xmlns="http://www.w3.org/2000/svg"> | |||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/> | |||||
</svg> |
@@ -0,0 +1,30 @@ | |||||
import Head from 'next/head'; | |||||
import { FC } from 'react'; | |||||
type DefaultLayoutProps = { | |||||
title?: string, | |||||
appName?: string, | |||||
} | |||||
export const DefaultLayout: FC<DefaultLayoutProps> = ({ | |||||
title, | |||||
appName = 'Tesseract Web (React)', | |||||
children, | |||||
}) => { | |||||
return ( | |||||
<> | |||||
<Head> | |||||
<title> | |||||
{title ? `${title} | ${appName}` : appName} | |||||
</title> | |||||
{ | |||||
title | |||||
&& ( | |||||
<meta name="display-title" className="block" content={title} /> | |||||
) | |||||
} | |||||
</Head> | |||||
{children} | |||||
</> | |||||
) | |||||
} |
@@ -0,0 +1,166 @@ | |||||
import * as React from 'react'; | |||||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||||
export enum ActionButtonType { | |||||
SUBMIT = 'submit', | |||||
RESET = 'reset', | |||||
BUTTON = 'button', | |||||
} | |||||
export type ActionButtonProps = 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, | |||||
/** | |||||
* Style of the component. | |||||
*/ | |||||
style?: ButtonBase.ButtonStyle, | |||||
/** | |||||
* 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, | |||||
style = ButtonBase.ButtonStyle.DEFAULT, | |||||
disabled = false, | |||||
compact = false, | |||||
subtext, | |||||
badge, | |||||
menuItem = false, | |||||
className: _className, | |||||
as: _as, | |||||
...etcProps | |||||
}: ActionButtonProps, | |||||
ref, | |||||
) => { | |||||
const styleProps = React.useMemo<ButtonBase.ButtonBaseArgs>(() => ({ | |||||
size, | |||||
block, | |||||
variant, | |||||
border, | |||||
style, | |||||
compact, | |||||
menuItem, | |||||
disabled, | |||||
}), [size, block, variant, border, style, compact, menuItem]); | |||||
return ( | |||||
<button | |||||
{...etcProps} | |||||
disabled={disabled} | |||||
className={ButtonBase.Button(styleProps)} | |||||
ref={ref} | |||||
type={type ?? ActionButtonType.BUTTON} | |||||
> | |||||
<span | |||||
className={ButtonBase.Border(styleProps)} | |||||
/> | |||||
<span | |||||
className={ButtonBase.Label(styleProps)} | |||||
> | |||||
<span | |||||
className={ButtonBase.MainText()} | |||||
> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{children} | |||||
</span> | |||||
</span> | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span className={ButtonBase.Subtext()}> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
{ | |||||
menuItem | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.IndicatorWrapper(styleProps)} | |||||
> | |||||
<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,0 +1 @@ | |||||
export * from './components/ActionButton'; |
@@ -0,0 +1,303 @@ | |||||
import { css } from '@tesseract-design/css-utils'; | |||||
export enum ButtonSize { | |||||
SMALL = 'small', | |||||
MEDIUM = 'medium', | |||||
LARGE = 'large', | |||||
} | |||||
export enum ButtonVariant { | |||||
OUTLINE = 'outline', | |||||
FILLED = 'filled', | |||||
} | |||||
export enum ButtonStyle { | |||||
DEFAULT = 'default', | |||||
} | |||||
export type ButtonBaseArgs = { | |||||
size: ButtonSize, | |||||
block: boolean, | |||||
variant: ButtonVariant, | |||||
border: boolean, | |||||
disabled: boolean, | |||||
style: ButtonStyle, | |||||
compact: boolean, | |||||
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-bg, white) !important; | |||||
` | |||||
), | |||||
css.if (variant === ButtonVariant.OUTLINE) ( | |||||
css` | |||||
background-color: var(--color-bg, 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,0 +1,310 @@ | |||||
import { css } from '@tesseract-design/css-utils' | |||||
export enum CheckControlAppearance { | |||||
DEFAULT = 'default', | |||||
BUTTON = 'button', | |||||
SWITCH = 'switch', | |||||
} | |||||
export type CheckControlBaseArgs = { | |||||
compact: boolean, | |||||
appearance: CheckControlAppearance, | |||||
block: boolean, | |||||
type: string, | |||||
} | |||||
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.DEFAULT | |||||
|| 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.DEFAULT | |||||
) ( | |||||
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.DEFAULT | |||||
) ( | |||||
css` | |||||
display: block; | |||||
` | |||||
), | |||||
), | |||||
css.nest('&:checked + * > :first-child + * > * > :first-child') ( | |||||
css.if ( | |||||
appearance === CheckControlAppearance.BUTTON | |||||
|| appearance === CheckControlAppearance.DEFAULT | |||||
) ( | |||||
css` | |||||
display: block; | |||||
` | |||||
), | |||||
), | |||||
); | |||||
export const ClickArea = (): string => css.cx( | |||||
css` | |||||
display: contents; | |||||
` | |||||
); | |||||
export const CheckIndicatorArea = ({ | |||||
compact, | |||||
appearance, | |||||
type, | |||||
}: CheckControlBaseArgs): string => css.cx( | |||||
css` | |||||
display: inline-grid; | |||||
vertical-align: middle; | |||||
place-content: center; | |||||
position: relative; | |||||
background-color: var(--color-bg, white); | |||||
box-shadow: 0 0 0 0.125rem var(--color-bg, 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.DEFAULT) ( | |||||
css` | |||||
width: 1.5em; | |||||
height: 1.5em; | |||||
`, | |||||
css.if (type === 'checkbox') ( | |||||
css` | |||||
border-radius: 0.25rem; | |||||
` | |||||
), | |||||
css.if (type === '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 === 'checkbox') ( | |||||
css` | |||||
border-radius: 0.25rem; | |||||
` | |||||
), | |||||
css.if (type === 'radio') ( | |||||
css` | |||||
border-radius: 50%; | |||||
` | |||||
), | |||||
), | |||||
css.if (appearance === CheckControlAppearance.SWITCH) ( | |||||
css` | |||||
width: 2.5em; | |||||
height: 1.5em; | |||||
border-radius: 0.75em; | |||||
` | |||||
), | |||||
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.DEFAULT | |||||
|| 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-bg, 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, | |||||
}: CheckControlBaseArgs) => css.cx( | |||||
css` | |||||
vertical-align: middle; | |||||
`, | |||||
css.dynamic({ | |||||
display: block ? 'block' : 'inline-block', | |||||
}), | |||||
css.if (appearance === CheckControlAppearance.DEFAULT) ( | |||||
css` | |||||
padding-left: 2.25rem; | |||||
text-indent: -2.25rem; | |||||
` | |||||
), | |||||
css.if (appearance === CheckControlAppearance.SWITCH) ( | |||||
css` | |||||
padding-left: 3.25rem; | |||||
text-indent: -3.25rem; | |||||
` | |||||
), | |||||
); | |||||
export const Subtext = () => css.cx( | |||||
css` | |||||
font-size: 0.875em; | |||||
` | |||||
); |
@@ -0,0 +1,367 @@ | |||||
import { css } from '@tesseract-design/css-utils'; | |||||
export enum TextControlSize { | |||||
SMALL = 'small', | |||||
MEDIUM = 'medium', | |||||
LARGE = 'large', | |||||
} | |||||
export enum TextControlStyle { | |||||
DEFAULT = 'default', | |||||
ALTERNATE = 'alternate', | |||||
} | |||||
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 = { | |||||
block: boolean, | |||||
style: TextControlStyle, | |||||
border: boolean, | |||||
indicator: boolean, | |||||
size: TextControlSize, | |||||
resizable: boolean, | |||||
predefinedValues: boolean, | |||||
} | |||||
export const ComponentBase = ({ | |||||
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; | |||||
max-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-bg, 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-fg, black); | |||||
} | |||||
&:disabled { | |||||
cursor: not-allowed; | |||||
opacity: 0.5; | |||||
} | |||||
&:disabled ~ * { | |||||
opacity: 0.5; | |||||
} | |||||
`, | |||||
css.media('only screen') ( | |||||
css` | |||||
background-color: var(--color-bg, white); | |||||
color: var(--color-fg, 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-bg, 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-bg, 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], | |||||
}), | |||||
); |
@@ -0,0 +1,124 @@ | |||||
import { css, CssIfStringImpl, CssStringImpl } from '.'; | |||||
describe('css-utils', () => { | |||||
describe('css', () => { | |||||
it('should return CssString', () => { | |||||
const c = css` | |||||
background-color: white; | |||||
color: black; | |||||
` | |||||
expect(c).toBeInstanceOf(CssStringImpl); | |||||
expect(c.toString()).toBe('background-color:white; color:black;'); | |||||
}) | |||||
}) | |||||
describe('css.if', () => { | |||||
it('should return CssString when the condition is true', () => { | |||||
const c = css.if(true)( | |||||
css` | |||||
background-color: white; | |||||
` | |||||
) | |||||
expect(c).toBeInstanceOf(CssIfStringImpl) | |||||
expect(c.toString()).toBe('background-color:white;') | |||||
}) | |||||
it('should return empty string when the condition is false', () => { | |||||
const c = css.if(false)( | |||||
css` | |||||
background-color: white; | |||||
` | |||||
) | |||||
expect(c.toString()).toBe('') | |||||
}) | |||||
it('should return CssString with .else when the condition is true', () => { | |||||
const c = css.if(false)( | |||||
css` | |||||
background-color: white; | |||||
` | |||||
).else( | |||||
css` | |||||
background-color: black; | |||||
` | |||||
) | |||||
expect(c).toBeInstanceOf(CssStringImpl) | |||||
expect(c.toString()).toBe('background-color:black;') | |||||
}) | |||||
it('should return CssString with .else.if when the condition is true', () => { | |||||
const c = css.if(false)( | |||||
css` | |||||
background-color: white; | |||||
` | |||||
).else.if(true)( | |||||
css` | |||||
background-color: black; | |||||
` | |||||
) | |||||
expect(c).toBeInstanceOf(CssIfStringImpl) | |||||
expect(c.toString()).toBe('background-color:black;') | |||||
}) | |||||
it('should return CssString with .else.if.else when the condition is false', () => { | |||||
const c = css.if('a'.toUpperCase() === 'C')( | |||||
css` | |||||
background-color: white; | |||||
` | |||||
).else.if('b'.toUpperCase() === 'C')( | |||||
css` | |||||
background-color: black; | |||||
` | |||||
).else( | |||||
css` | |||||
background-color: gray; | |||||
` | |||||
) | |||||
expect(c).toBeInstanceOf(CssStringImpl) | |||||
expect(c.toString()).toBe('background-color:gray;') | |||||
}) | |||||
}) | |||||
describe('css.nest', () => { | |||||
it('should add selector', () => { | |||||
const c = css.nest('> div')( | |||||
css` | |||||
background-color: white; | |||||
` | |||||
) | |||||
expect(c.toString()).toBe('> div{background-color:white;}') | |||||
}) | |||||
}) | |||||
describe('css.dynamic', () => { | |||||
it('should evaluate dynamic object', () => { | |||||
const randomNumber = Math.floor(Math.random() * 1000); | |||||
const c = css.dynamic({ | |||||
'line-height': randomNumber, | |||||
}) | |||||
expect(c.toString()).toBe(`line-height:${randomNumber};`) | |||||
}) | |||||
}) | |||||
describe('css.media', () => { | |||||
it('should accept raw queries', () => { | |||||
const c = css.media('only screen and (min-width: 720px)')( | |||||
css` | |||||
color: black; | |||||
background-color: white; | |||||
` | |||||
) | |||||
expect(c.toString()).toBe('@media only screen and (min-width: 720px){color:black; background-color:white;}') | |||||
}) | |||||
}) | |||||
}) |
@@ -0,0 +1,143 @@ | |||||
import { css as gooberCss } from 'goober'; | |||||
import { PropertiesHyphenFallback } from 'csstype'; | |||||
interface CssString { | |||||
// TODO stricter type checking | |||||
toString(): string | |||||
} | |||||
export class CssStringImpl implements CssString { | |||||
private css: string | |||||
constructor(s: TemplateStringsArray) { | |||||
this.css = s.raw[0] | |||||
.trim() | |||||
.replace(/[ ][ ]+/g, ' ') | |||||
.replace(/:[ ]/g, ':') | |||||
.replace(/\n/g, '') | |||||
.replace(/;[ ]/g, ';'); | |||||
} | |||||
toString() { | |||||
return this.css | |||||
} | |||||
} | |||||
interface CssIf { | |||||
(b: boolean): (...a: CssString[]) => CssIfString | |||||
} | |||||
interface CssElse { | |||||
(...c: CssString[]): CssString | |||||
if: CssIf | |||||
} | |||||
interface CssIfString extends CssString { | |||||
else: CssElse | |||||
} | |||||
const cssIf: CssIf = (b: boolean) => (...a: CssString[]) => new CssIfStringImpl(b, ...a); | |||||
export class CssIfStringImpl implements CssIfString { | |||||
readonly else: CssElse | |||||
private readonly cssStrings: CssString[] | |||||
constructor(private readonly condition: boolean, ...cssStrings: CssString[]) { | |||||
this.cssStrings = cssStrings | |||||
const elseFn = (...c: CssString[]) => { | |||||
if (this.condition) { | |||||
return { | |||||
toString: () => { | |||||
return this.cssStrings.map((c2) => c2.toString()).join(''); | |||||
} | |||||
} | |||||
} | |||||
return { | |||||
toString: () => { | |||||
return c.map((cc) => cc.toString()).join('') | |||||
} | |||||
}; | |||||
} | |||||
elseFn.if = cssIf | |||||
this.else = elseFn | |||||
} | |||||
toString() { | |||||
if (this.condition) { | |||||
return this.cssStrings.map((c2) => c2.toString()).join(''); | |||||
} | |||||
return ''; | |||||
} | |||||
} | |||||
interface CssNest { | |||||
(selector: string): (...a: CssString[]) => CssString | |||||
} | |||||
const cssNest: CssNest = (selector) => (...a) => { | |||||
return { | |||||
toString: () => `${selector}{${a.map(aa => aa.toString()).join('')}}` | |||||
} | |||||
} | |||||
interface CssDynamic { | |||||
(a: PropertiesHyphenFallback): CssString | |||||
} | |||||
const cssDynamic: CssDynamic = (a: PropertiesHyphenFallback) => { | |||||
return { | |||||
toString(): string { | |||||
return Object | |||||
.entries(a) | |||||
.map(([key, value]) => `${key}:${value.toString()};`) | |||||
.join('') | |||||
} | |||||
}; | |||||
}; | |||||
interface CssMedia { | |||||
(raw: string): any | |||||
} | |||||
const cssMedia: CssMedia = (arg1: string) => { | |||||
return (...body: CssString[]) => { | |||||
return { | |||||
toString(): string { | |||||
return `@media ${arg1}{${body.map(b => b.toString()).join('')}}` | |||||
} | |||||
} | |||||
} | |||||
} | |||||
const cssCompile = (...strings: CssString[]) => { | |||||
return strings | |||||
.filter((s) => ['string', 'object'].includes(typeof s)) | |||||
.map((s) => { | |||||
if (typeof s === 'object') { | |||||
return gooberCss`${s.toString()}` | |||||
} | |||||
return s | |||||
}) | |||||
.join(' ') | |||||
} | |||||
interface Css { | |||||
(s: TemplateStringsArray): CssString | |||||
if: CssIf | |||||
nest: CssNest | |||||
dynamic: CssDynamic | |||||
media: CssMedia | |||||
cx(...strings: CssString[]): string | |||||
} | |||||
const _css: Partial<Css> = (s: TemplateStringsArray) => new CssStringImpl(s); | |||||
_css.if = cssIf; | |||||
_css.nest = cssNest; | |||||
_css.dynamic = cssDynamic; | |||||
_css.media = cssMedia; | |||||
_css.cx = cssCompile; | |||||
export const css = _css as Css; |
@@ -0,0 +1,18 @@ | |||||
import * as React from 'react'; | |||||
import { | |||||
render, | |||||
screen | |||||
} from '@testing-library/react'; | |||||
import '@testing-library/jest-dom'; | |||||
import { | |||||
MaskedTextInput | |||||
} from '.'; | |||||
describe('MaskedTextInput', () => { | |||||
it('should render an input', () => { | |||||
render(<MaskedTextInput />); | |||||
const textbox: HTMLInputElement = screen.getByTestId('input'); | |||||
expect(textbox).toBeInTheDocument(); | |||||
expect(textbox.type).toBe('password'); | |||||
}); | |||||
}); |
@@ -0,0 +1,117 @@ | |||||
import * as React from 'react'; | |||||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||||
export type MaskedTextInputProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style'> & { | |||||
/** | |||||
* 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. | |||||
*/ | |||||
style?: TextControlBase.TextControlStyle, | |||||
/** | |||||
* 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<HTMLInputElement, MaskedTextInputProps>( | |||||
( | |||||
{ | |||||
label = '', | |||||
hint = '', | |||||
indicator = null, | |||||
size = TextControlBase.TextControlSize.MEDIUM, | |||||
border = false, | |||||
block = false, | |||||
style = TextControlBase.TextControlStyle.DEFAULT, | |||||
hiddenLabel = false, | |||||
className: _className, | |||||
placeholder: _placeholder, | |||||
as: _as, | |||||
...etcProps | |||||
}: MaskedTextInputProps, | |||||
ref, | |||||
) => { | |||||
const textInputBaseArgs = React.useMemo<TextControlBase.TextControlBaseArgs>(() => ({ | |||||
block, | |||||
border, | |||||
size, | |||||
indicator: Boolean(indicator), | |||||
style, | |||||
resizable: false, | |||||
predefinedValues: false, | |||||
}), [block, border, size, indicator, style]); | |||||
return ( | |||||
<div | |||||
className={TextControlBase.ComponentBase(textInputBaseArgs)} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
className={TextControlBase.Input(textInputBaseArgs)} | |||||
ref={ref} | |||||
aria-label={label} | |||||
type="password" | |||||
data-testid="input" | |||||
/> | |||||
{border && <span />} | |||||
{ | |||||
label && !hiddenLabel && ( | |||||
<div | |||||
className={TextControlBase.LabelWrapper(textInputBaseArgs)} | |||||
> | |||||
{label} | |||||
</div> | |||||
) | |||||
} | |||||
{hint && ( | |||||
<div | |||||
className={TextControlBase.HintWrapper(textInputBaseArgs)} | |||||
> | |||||
<div | |||||
className={TextControlBase.Hint()} | |||||
> | |||||
{hint} | |||||
</div> | |||||
</div> | |||||
)} | |||||
{indicator && ( | |||||
<div | |||||
className={TextControlBase.IndicatorWrapper(textInputBaseArgs)} | |||||
> | |||||
{indicator} | |||||
</div> | |||||
)} | |||||
</div> | |||||
); | |||||
}, | |||||
); | |||||
MaskedTextInput.displayName = 'MaskedTextInput'; |
@@ -0,0 +1,17 @@ | |||||
import * as React from 'react'; | |||||
import { | |||||
render, | |||||
screen | |||||
} from '@testing-library/react'; | |||||
import '@testing-library/jest-dom'; | |||||
import { | |||||
MultilineTextInput | |||||
} from '.'; | |||||
describe('MultilineTextInput', () => { | |||||
it('should render a textbox', () => { | |||||
render(<MultilineTextInput />); | |||||
const textbox = screen.getByRole('textbox'); | |||||
expect(textbox).toBeInTheDocument(); | |||||
}); | |||||
}); |
@@ -0,0 +1,118 @@ | |||||
import * as React from 'react'; | |||||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||||
export type MultilineTextInputProps = Omit<React.HTMLProps<HTMLTextAreaElement>, 'size' | 'style'> & { | |||||
/** | |||||
* 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. | |||||
*/ | |||||
style?: TextControlBase.TextControlStyle, | |||||
/** | |||||
* 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<HTMLTextAreaElement, MultilineTextInputProps>( | |||||
( | |||||
{ | |||||
label = '', | |||||
hint = '', | |||||
indicator = null, | |||||
size = TextControlBase.TextControlSize.MEDIUM, | |||||
border = false, | |||||
block = false, | |||||
style = TextControlBase.TextControlStyle.DEFAULT, | |||||
hiddenLabel = false, | |||||
className: _className, | |||||
placeholder: _placeholder, | |||||
as: _as, | |||||
...etcProps | |||||
}: MultilineTextInputProps, | |||||
ref, | |||||
) => { | |||||
const textInputBaseArgs = React.useMemo<TextControlBase.TextControlBaseArgs>(() => ({ | |||||
block, | |||||
border, | |||||
size, | |||||
indicator: Boolean(indicator), | |||||
style, | |||||
resizable: true, | |||||
predefinedValues: false, | |||||
}), [block, border, size, indicator, style]); | |||||
return ( | |||||
<div | |||||
className={TextControlBase.ComponentBase(textInputBaseArgs)} | |||||
> | |||||
<textarea | |||||
{...etcProps} | |||||
className={TextControlBase.Input(textInputBaseArgs)} | |||||
ref={ref} | |||||
aria-label={label} | |||||
style={{ | |||||
height: TextControlBase.MIN_HEIGHTS[size], | |||||
}} | |||||
/> | |||||
{border && <span />} | |||||
{ | |||||
label && !hiddenLabel && ( | |||||
<div | |||||
className={TextControlBase.LabelWrapper(textInputBaseArgs)} | |||||
> | |||||
{label} | |||||
</div> | |||||
) | |||||
} | |||||
{hint && ( | |||||
<div | |||||
className={TextControlBase.HintWrapper(textInputBaseArgs)} | |||||
> | |||||
<div | |||||
className={TextControlBase.Hint()} | |||||
> | |||||
{hint} | |||||
</div> | |||||
</div> | |||||
)} | |||||
{indicator && ( | |||||
<div | |||||
className={TextControlBase.IndicatorWrapper(textInputBaseArgs)} | |||||
> | |||||
{indicator} | |||||
</div> | |||||
)} | |||||
</div> | |||||
); | |||||
} | |||||
); | |||||
MultilineTextInput.displayName = 'MultilineTextInput'; |
@@ -0,0 +1,17 @@ | |||||
import * as React from 'react'; | |||||
import { | |||||
render, | |||||
screen | |||||
} from '@testing-library/react'; | |||||
import '@testing-library/jest-dom'; | |||||
import { | |||||
TextInput | |||||
} from '.'; | |||||
describe('TextInput', () => { | |||||
it('should render a textbox', () => { | |||||
render(<TextInput />); | |||||
const textbox = screen.getByRole('textbox'); | |||||
expect(textbox).toBeInTheDocument(); | |||||
}); | |||||
}); |
@@ -0,0 +1,126 @@ | |||||
import * as React from 'react'; | |||||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||||
export enum TextInputType { | |||||
TEXT = 'text', | |||||
SEARCH = 'search', | |||||
} | |||||
export type TextInputProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style'> & { | |||||
/** | |||||
* 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?: TextInputType, | |||||
/** | |||||
* Style of the component. | |||||
*/ | |||||
style?: TextControlBase.TextControlStyle, | |||||
/** | |||||
* 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<HTMLInputElement, TextInputProps>( | |||||
( | |||||
{ | |||||
label = '', | |||||
hint = '', | |||||
indicator = null, | |||||
size = TextControlBase.TextControlSize.MEDIUM, | |||||
border = false, | |||||
block = false, | |||||
type = TextInputType.TEXT, | |||||
style = TextControlBase.TextControlStyle.DEFAULT, | |||||
hiddenLabel = false, | |||||
className: _className, | |||||
placeholder: _placeholder, | |||||
as: _as, | |||||
...etcProps | |||||
}: TextInputProps, | |||||
ref, | |||||
) => { | |||||
const textInputBaseArgs = React.useMemo<TextControlBase.TextControlBaseArgs>(() => ({ | |||||
block, | |||||
border, | |||||
size, | |||||
indicator: Boolean(indicator), | |||||
style, | |||||
resizable: false, | |||||
predefinedValues: false, | |||||
}), [block, border, size, indicator, style]); | |||||
return ( | |||||
<div | |||||
className={TextControlBase.ComponentBase(textInputBaseArgs)} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
className={TextControlBase.Input(textInputBaseArgs)} | |||||
ref={ref} | |||||
aria-label={label} | |||||
type={type} | |||||
/> | |||||
{border && <span />} | |||||
{ | |||||
label && !hiddenLabel && ( | |||||
<div | |||||
className={TextControlBase.LabelWrapper(textInputBaseArgs)} | |||||
> | |||||
{label} | |||||
</div> | |||||
) | |||||
} | |||||
{hint && ( | |||||
<div | |||||
className={TextControlBase.HintWrapper(textInputBaseArgs)} | |||||
> | |||||
<div | |||||
className={TextControlBase.Hint()} | |||||
> | |||||
{hint} | |||||
</div> | |||||
</div> | |||||
)} | |||||
{indicator && ( | |||||
<div | |||||
className={TextControlBase.IndicatorWrapper(textInputBaseArgs)} | |||||
> | |||||
{indicator} | |||||
</div> | |||||
)} | |||||
</div> | |||||
); | |||||
} | |||||
); | |||||
TextInput.displayName = 'TextInput'; |
@@ -0,0 +1,3 @@ | |||||
export * from './components/MultilineTextInput'; | |||||
export * from './components/MaskedTextInput'; | |||||
export * from './components/TextInput'; |
@@ -0,0 +1,66 @@ | |||||
import * as React from 'react'; | |||||
import { | |||||
css, | |||||
} from 'goober'; | |||||
const BadgeBase = ({ rounded }: { rounded: boolean }) => css` | |||||
position: relative; | |||||
height: 1.5em; | |||||
min-width: 1.5em; | |||||
display: inline-grid; | |||||
vertical-align: middle; | |||||
place-content: center; | |||||
border-radius: ${rounded ? '0.75em' : '0.25rem'}; | |||||
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: ''; | |||||
} | |||||
`; | |||||
const Content = () => css` | |||||
position: relative; | |||||
font-size: 0.75em; | |||||
`; | |||||
export type BadgeProps = React.HTMLProps<HTMLSpanElement> & { | |||||
rounded?: boolean, | |||||
}; | |||||
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>( | |||||
( | |||||
{ | |||||
children, | |||||
rounded = false, | |||||
}, | |||||
ref, | |||||
) => { | |||||
const badgeStyleProps = React.useMemo(() => ({ | |||||
rounded, | |||||
}), [rounded]); | |||||
return ( | |||||
<strong | |||||
ref={ref} | |||||
className={BadgeBase(badgeStyleProps)} | |||||
> | |||||
<span | |||||
className={Content()} | |||||
> | |||||
{children} | |||||
</span> | |||||
</strong> | |||||
) | |||||
} | |||||
) | |||||
Badge.displayName = 'Badge'; |
@@ -0,0 +1 @@ | |||||
export * from './components/Badge'; |
@@ -0,0 +1,189 @@ | |||||
import * as React from 'react'; | |||||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||||
type LinkButtonElement = HTMLAnchorElement | HTMLSpanElement; | |||||
export type LinkButtonProps = Omit<React.HTMLProps<LinkButtonElement>, 'size' | '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, | |||||
/** | |||||
* Can the component be activated? | |||||
*/ | |||||
disabled?: boolean, | |||||
/** | |||||
* Style of the component. | |||||
*/ | |||||
style?: ButtonBase.ButtonStyle, | |||||
/** | |||||
* 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 hyperlink. | |||||
*/ | |||||
export const LinkButton = React.forwardRef<LinkButtonElement, LinkButtonProps>( | |||||
( | |||||
{ | |||||
size = ButtonBase.ButtonSize.MEDIUM, | |||||
variant = ButtonBase.ButtonVariant.OUTLINE, | |||||
border = false, | |||||
children, | |||||
block = false, | |||||
disabled = false, | |||||
style = ButtonBase.ButtonStyle.DEFAULT, | |||||
onClick, | |||||
href, | |||||
target, | |||||
rel, | |||||
compact = false, | |||||
subtext, | |||||
badge, | |||||
menuItem = false, | |||||
className: _className, | |||||
as: _as, | |||||
...etcProps | |||||
}: LinkButtonProps, | |||||
ref, | |||||
) => { | |||||
const styleProps = React.useMemo<ButtonBase.ButtonBaseArgs>(() => ({ | |||||
size, | |||||
block, | |||||
variant, | |||||
border, | |||||
disabled, | |||||
style, | |||||
compact, | |||||
menuItem, | |||||
}), [size, block, variant, border, disabled, style, compact, menuItem]); | |||||
const commonChildren = ( | |||||
<> | |||||
<span | |||||
className={ButtonBase.Border(styleProps)} | |||||
/> | |||||
<span | |||||
className={ButtonBase.Label(styleProps)} | |||||
> | |||||
<span | |||||
className={ButtonBase.MainText()} | |||||
> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{children} | |||||
</span> | |||||
</span> | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.Subtext()} | |||||
> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
{ | |||||
menuItem | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.IndicatorWrapper(styleProps)} | |||||
> | |||||
<svg | |||||
className={ButtonBase.Indicator()} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline points="9 18 15 12 9 6"/> | |||||
</svg> | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</> | |||||
); | |||||
if (disabled) { | |||||
return ( | |||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events | |||||
<span | |||||
{...etcProps} | |||||
className={ButtonBase.Button(styleProps)} | |||||
ref={ref as React.ForwardedRef<HTMLSpanElement>} | |||||
onClick={undefined} | |||||
> | |||||
{commonChildren} | |||||
</span> | |||||
); | |||||
} | |||||
return ( | |||||
<a | |||||
{...etcProps} | |||||
className={ButtonBase.Button(styleProps)} | |||||
ref={ref as React.ForwardedRef<HTMLAnchorElement>} | |||||
href={href} | |||||
target={target} | |||||
rel={rel} | |||||
onClick={onClick} | |||||
> | |||||
{commonChildren} | |||||
</a> | |||||
); | |||||
}, | |||||
); | |||||
LinkButton.displayName = 'LinkButton'; |
@@ -0,0 +1 @@ | |||||
export * from './components/LinkButton'; |
@@ -0,0 +1,277 @@ | |||||
import * as React from 'react'; | |||||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||||
export type CheckboxProps = Omit<React.HTMLProps<HTMLInputElement>, '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, | |||||
/** | |||||
* Style of the component. | |||||
*/ | |||||
style?: ButtonBase.ButtonStyle, | |||||
/** | |||||
* 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, | |||||
/** | |||||
* Appearance of the component. | |||||
*/ | |||||
appearance?: CheckControlBase.CheckControlAppearance, | |||||
/** | |||||
* Does the component have indeterminate check state? | |||||
*/ | |||||
indeterminate?: boolean, | |||||
} | |||||
/** | |||||
* Component for performing an action upon activation (e.g. when clicked). | |||||
* | |||||
* This component functions as a regular button. | |||||
*/ | |||||
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>( | |||||
( | |||||
{ | |||||
size = ButtonBase.ButtonSize.MEDIUM, | |||||
variant = ButtonBase.ButtonVariant.OUTLINE, | |||||
border = false, | |||||
children, | |||||
block = false, | |||||
style = ButtonBase.ButtonStyle.DEFAULT, | |||||
disabled = false, | |||||
compact = false, | |||||
subtext, | |||||
badge, | |||||
appearance = CheckControlBase.CheckControlAppearance.DEFAULT, | |||||
indeterminate = false, | |||||
className: _className, | |||||
as: _as, | |||||
...etcProps | |||||
}: CheckboxProps, | |||||
ref, | |||||
) => { | |||||
const styleProps = React.useMemo<ButtonBase.ButtonBaseArgs & CheckControlBase.CheckControlBaseArgs>(() => ({ | |||||
size, | |||||
block, | |||||
variant, | |||||
border, | |||||
style, | |||||
compact, | |||||
menuItem: false, | |||||
disabled, | |||||
appearance, | |||||
type: 'checkbox', | |||||
}), [size, block, variant, border, style, compact, disabled, appearance]); | |||||
const defaultRef = React.useRef<HTMLInputElement>(null); | |||||
const theRef = (ref ?? defaultRef) as React.MutableRefObject<HTMLInputElement>; | |||||
React.useEffect(() => { | |||||
if (!(indeterminate && theRef.current)) { | |||||
return; | |||||
} | |||||
theRef.current.indeterminate = indeterminate; | |||||
}, [theRef, indeterminate]); | |||||
const checkIndicatorCommon = ( | |||||
<span | |||||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||||
> | |||||
<span | |||||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||||
> | |||||
<svg | |||||
className={CheckControlBase.CheckIndicator(styleProps)} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline | |||||
points="20 6 9 17 4 12" | |||||
/> | |||||
</svg> | |||||
<svg | |||||
className={CheckControlBase.CheckIndicator(styleProps)} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline | |||||
points="20 12 4 12" | |||||
/> | |||||
</svg> | |||||
</span> | |||||
</span> | |||||
) | |||||
return ( | |||||
<div | |||||
className={CheckControlBase.ClickAreaWrapper(styleProps)} | |||||
> | |||||
<label | |||||
className={CheckControlBase.ClickArea()} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
disabled={disabled} | |||||
type="checkbox" | |||||
ref={theRef} | |||||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||||
/> | |||||
{ | |||||
appearance === CheckControlBase.CheckControlAppearance.DEFAULT | |||||
&& ( | |||||
<span> | |||||
<span /> | |||||
{checkIndicatorCommon} | |||||
<span> | |||||
{children} | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
<br /> | |||||
<span | |||||
className={CheckControlBase.Subtext()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
) | |||||
} | |||||
{ | |||||
appearance === CheckControlBase.CheckControlAppearance.BUTTON | |||||
&& ( | |||||
<span | |||||
className={ButtonBase.Button(styleProps)} | |||||
> | |||||
<span | |||||
className={ButtonBase.Border(styleProps)} | |||||
/> | |||||
{checkIndicatorCommon} | |||||
<span | |||||
className={ButtonBase.Label(styleProps)} | |||||
> | |||||
<span | |||||
className={ButtonBase.MainText()} | |||||
> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{children} | |||||
</span> | |||||
</span> | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.Subtext()} | |||||
> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
) | |||||
} | |||||
{ | |||||
appearance === CheckControlBase.CheckControlAppearance.SWITCH | |||||
&& ( | |||||
<span> | |||||
<span /> | |||||
{checkIndicatorCommon} | |||||
<span> | |||||
{children} | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
<br /> | |||||
<span | |||||
className={CheckControlBase.Subtext()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
) | |||||
} | |||||
</label> | |||||
</div> | |||||
); | |||||
}, | |||||
); | |||||
Checkbox.displayName = 'ActionButton'; |
@@ -0,0 +1,17 @@ | |||||
import * as React from 'react'; | |||||
import { | |||||
render, | |||||
screen | |||||
} from '@testing-library/react'; | |||||
import '@testing-library/jest-dom'; | |||||
import { | |||||
DropdownSelect | |||||
} from '.'; | |||||
describe('DropdownSelect', () => { | |||||
it('should render a combobox', () => { | |||||
render(<DropdownSelect />); | |||||
const combobox = screen.getByRole('combobox'); | |||||
expect(combobox).toBeInTheDocument(); | |||||
}); | |||||
}); |
@@ -0,0 +1,218 @@ | |||||
import * as React from 'react'; | |||||
import { | |||||
css | |||||
} from 'goober'; | |||||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||||
const Indicator = () => css` | |||||
width: 1.5em; | |||||
height: 1.5em; | |||||
fill: none; | |||||
stroke: currentColor; | |||||
stroke-width: 2; | |||||
stroke-linecap: round; | |||||
stroke-linejoin: round; | |||||
`; | |||||
export interface SelectOption { | |||||
label: string, | |||||
value?: string | number | readonly string[] | |||||
children?: SelectOption[] | |||||
} | |||||
type RenderOptionsProps = { | |||||
options: SelectOption[], | |||||
// FIXME bug in eslint does not play well with React.VFC | |||||
// eslint-disable-next-line react/require-default-props | |||||
optionComponent?: React.ElementType, | |||||
// eslint-disable-next-line react/require-default-props | |||||
optgroupComponent?: React.ElementType, | |||||
// eslint-disable-next-line react/require-default-props | |||||
level?: number, | |||||
} | |||||
const RenderOptions: React.VFC<RenderOptionsProps> = ({ | |||||
options, | |||||
optionComponent: Option = 'option', | |||||
optgroupComponent: Optgroup = 'optgroup', | |||||
level = 0, | |||||
}: RenderOptionsProps) => ( | |||||
<> | |||||
{ | |||||
options.map((o) => { | |||||
if (typeof o.value !== 'undefined') { | |||||
return ( | |||||
<Option | |||||
key={`${o.label}:${o.value.toString()}`} | |||||
value={o.value} | |||||
> | |||||
{o.label} | |||||
</Option> | |||||
); | |||||
} | |||||
if (typeof o.children !== 'undefined') { | |||||
if (level === 0) { | |||||
return ( | |||||
<Optgroup | |||||
key={o.label} | |||||
label={o.label} | |||||
> | |||||
<RenderOptions | |||||
options={o.children} | |||||
optionComponent={Option} | |||||
optgroupComponent={Optgroup} | |||||
/> | |||||
</Optgroup> | |||||
); | |||||
} | |||||
return ( | |||||
<React.Fragment | |||||
key={o.label} | |||||
> | |||||
<Option | |||||
disabled | |||||
> | |||||
{o.label} | |||||
</Option> | |||||
<RenderOptions | |||||
options={o.children} | |||||
optionComponent={Option} | |||||
optgroupComponent={Optgroup} | |||||
/> | |||||
</React.Fragment> | |||||
); | |||||
} | |||||
return null; | |||||
}) | |||||
} | |||||
</> | |||||
); | |||||
export type DropdownSelectProps = Omit<React.HTMLProps<HTMLSelectElement>, 'size' | 'style' | 'children'> & { | |||||
/** | |||||
* 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, | |||||
/** | |||||
* Should the component display a border? | |||||
*/ | |||||
border?: boolean, | |||||
/** | |||||
* Should the component occupy the whole width of its parent? | |||||
*/ | |||||
block?: boolean, | |||||
/** | |||||
* Style of the component. | |||||
*/ | |||||
style?: TextControlBase.TextControlStyle, | |||||
/** | |||||
* Is the label hidden? | |||||
*/ | |||||
hiddenLabel?: boolean, | |||||
/** | |||||
* Options available for the component's values. | |||||
*/ | |||||
options?: SelectOption[], | |||||
/** | |||||
* Component for rendering options. | |||||
*/ | |||||
renderOptions?: React.ElementType, | |||||
} | |||||
/** | |||||
* Component for inputting textual values. | |||||
* | |||||
* This component supports multiline input and adjusts its layout accordingly. | |||||
*/ | |||||
export const DropdownSelect = React.forwardRef<HTMLSelectElement, DropdownSelectProps>( | |||||
( | |||||
{ | |||||
label = '', | |||||
hint = '', | |||||
size = TextControlBase.TextControlSize.MEDIUM, | |||||
border = false, | |||||
block = false, | |||||
style = TextControlBase.TextControlStyle.DEFAULT, | |||||
hiddenLabel = false, | |||||
multiple: _multiple, | |||||
className: _className, | |||||
placeholder: _placeholder, | |||||
as: _as, | |||||
options = [], | |||||
renderOptions: Render = RenderOptions, | |||||
...etcProps | |||||
}: DropdownSelectProps, | |||||
ref, | |||||
) => { | |||||
const styleArgs = React.useMemo<TextControlBase.TextControlBaseArgs>(() => ({ | |||||
block, | |||||
border, | |||||
size, | |||||
indicator: true, | |||||
style, | |||||
resizable: true, | |||||
predefinedValues: true, | |||||
}), [block, border, size, style]); | |||||
return ( | |||||
<div | |||||
className={TextControlBase.ComponentBase(styleArgs)} | |||||
> | |||||
<select | |||||
{...etcProps} | |||||
className={TextControlBase.Input(styleArgs)} | |||||
ref={ref} | |||||
aria-label={label} | |||||
> | |||||
<Render | |||||
options={options} | |||||
/> | |||||
</select> | |||||
{border && <span />} | |||||
{label && !hiddenLabel && ( | |||||
<div | |||||
className={TextControlBase.LabelWrapper(styleArgs)} | |||||
> | |||||
{label} | |||||
</div> | |||||
)} | |||||
{hint && ( | |||||
<div | |||||
className={TextControlBase.HintWrapper(styleArgs)} | |||||
> | |||||
<div | |||||
className={TextControlBase.Hint()} | |||||
> | |||||
{hint} | |||||
</div> | |||||
</div> | |||||
)} | |||||
<div | |||||
className={TextControlBase.IndicatorWrapper(styleArgs)} | |||||
> | |||||
<svg | |||||
className={Indicator()} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline | |||||
points="6 9 12 15 18 9" | |||||
/> | |||||
</svg> | |||||
</div> | |||||
</div> | |||||
); | |||||
} | |||||
); | |||||
DropdownSelect.displayName = 'DropdownSelect'; |
@@ -0,0 +1,250 @@ | |||||
import * as React from 'react'; | |||||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||||
export type RadioButtonProps = Omit<React.HTMLProps<HTMLInputElement>, '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, | |||||
/** | |||||
* Style of the component. | |||||
*/ | |||||
style?: ButtonBase.ButtonStyle, | |||||
/** | |||||
* 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, | |||||
/** | |||||
* Appearance of the component. | |||||
*/ | |||||
appearance?: CheckControlBase.CheckControlAppearance, | |||||
} | |||||
/** | |||||
* Component for performing an action upon activation (e.g. when clicked). | |||||
* | |||||
* This component functions as a regular button. | |||||
*/ | |||||
export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>( | |||||
( | |||||
{ | |||||
size = ButtonBase.ButtonSize.MEDIUM, | |||||
variant = ButtonBase.ButtonVariant.OUTLINE, | |||||
border = false, | |||||
children, | |||||
block = false, | |||||
style = ButtonBase.ButtonStyle.DEFAULT, | |||||
disabled = false, | |||||
compact = false, | |||||
subtext, | |||||
badge, | |||||
appearance = CheckControlBase.CheckControlAppearance.DEFAULT, | |||||
className: _className, | |||||
as: _as, | |||||
...etcProps | |||||
}: RadioButtonProps, | |||||
ref, | |||||
) => { | |||||
const styleProps = React.useMemo<ButtonBase.ButtonBaseArgs & CheckControlBase.CheckControlBaseArgs>(() => ({ | |||||
size, | |||||
block, | |||||
variant, | |||||
border, | |||||
style, | |||||
compact, | |||||
menuItem: false, | |||||
disabled, | |||||
appearance, | |||||
type: 'radio', | |||||
}), [size, block, variant, border, style, compact, disabled, appearance]); | |||||
const checkIndicatorCommon = ( | |||||
<span | |||||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||||
> | |||||
<span | |||||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||||
/> | |||||
</span> | |||||
) | |||||
return ( | |||||
<div | |||||
className={CheckControlBase.ClickAreaWrapper(styleProps)} | |||||
> | |||||
<label | |||||
className={CheckControlBase.ClickArea()} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
disabled={disabled} | |||||
type="radio" | |||||
ref={ref} | |||||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||||
/> | |||||
{ | |||||
appearance === CheckControlBase.CheckControlAppearance.DEFAULT | |||||
&& ( | |||||
<span> | |||||
<span /> | |||||
{checkIndicatorCommon} | |||||
<span> | |||||
{children} | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
<br /> | |||||
<span | |||||
className={CheckControlBase.Subtext()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
) | |||||
} | |||||
{ | |||||
appearance === CheckControlBase.CheckControlAppearance.BUTTON | |||||
&& ( | |||||
<span | |||||
className={ButtonBase.Button(styleProps)} | |||||
> | |||||
<span | |||||
className={ButtonBase.Border(styleProps)} | |||||
/> | |||||
{checkIndicatorCommon} | |||||
<span | |||||
className={ButtonBase.Label(styleProps)} | |||||
> | |||||
<span | |||||
className={ButtonBase.MainText()} | |||||
> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{children} | |||||
</span> | |||||
</span> | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.Subtext()} | |||||
> | |||||
<span | |||||
className={ButtonBase.OverflowText()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
) | |||||
} | |||||
{ | |||||
appearance === CheckControlBase.CheckControlAppearance.SWITCH | |||||
&& ( | |||||
<span> | |||||
<span /> | |||||
<span | |||||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||||
> | |||||
<span | |||||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||||
/> | |||||
</span> | |||||
<span> | |||||
{children} | |||||
{ | |||||
subtext | |||||
&& ( | |||||
<> | |||||
<br /> | |||||
<span | |||||
className={CheckControlBase.Subtext()} | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
{ | |||||
badge | |||||
&& ( | |||||
<> | |||||
{' '} | |||||
<span | |||||
className={ButtonBase.BadgeContainer(styleProps)} | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
) | |||||
} | |||||
</span> | |||||
) | |||||
} | |||||
</label> | |||||
</div> | |||||
); | |||||
}, | |||||
); | |||||
RadioButton.displayName = 'ActionButton'; |
@@ -0,0 +1,3 @@ | |||||
export * from './components/Checkbox'; | |||||
export * from './components/DropdownSelect'; | |||||
export * from './components/RadioButton'; |
@@ -0,0 +1,13 @@ | |||||
import * as goober from 'goober'; | |||||
import * as gooberPrefixer from 'goober/prefixer'; | |||||
import type { AppProps } from 'next/app'; | |||||
import * as React from 'react'; | |||||
goober.setup(React.createElement, gooberPrefixer.prefix); | |||||
const App: React.VFC<AppProps> = ({ Component, pageProps }) => { | |||||
return <Component {...pageProps} /> | |||||
} | |||||
export default App |
@@ -0,0 +1,48 @@ | |||||
import NextDocument, {Html, Head as NextHead, Main, NextScript, DocumentContext} from 'next/document'; | |||||
import * as goober from 'goober'; | |||||
export default class Document extends NextDocument { | |||||
static async getInitialProps(ctx: DocumentContext) { | |||||
const page = await ctx.renderPage() | |||||
const style = goober.extractCss(); | |||||
const initialProps = await NextDocument.getInitialProps(ctx) | |||||
return { | |||||
...initialProps, | |||||
...page, | |||||
style, | |||||
} | |||||
} | |||||
render() { | |||||
const { style: rawStyle } = this.props as Record<string, unknown> | |||||
const style = rawStyle as string | |||||
return ( | |||||
<Html> | |||||
<NextHead | |||||
className="container mx-auto px-4 block mt-16 md:mt-32" | |||||
> | |||||
<link rel="stylesheet" href="/tailwind-utilities.css" /> | |||||
<link rel="stylesheet" href="/global.css" /> | |||||
<link rel="stylesheet" href="/theme.css" /> | |||||
<link rel="stylesheet" href="/theme/dark.css" /> | |||||
{ | |||||
style.length > 0 | |||||
&& ( | |||||
<style | |||||
id="_goober_ssr" | |||||
dangerouslySetInnerHTML={{ __html: style }} | |||||
/> | |||||
) | |||||
} | |||||
</NextHead> | |||||
<body> | |||||
<Main /> | |||||
<NextScript /> | |||||
</body> | |||||
</Html> | |||||
) | |||||
} | |||||
} |
@@ -0,0 +1,251 @@ | |||||
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'; | |||||
const ActionPage: NextPage = () => { | |||||
return ( | |||||
<DefaultLayout | |||||
title="Action" | |||||
> | |||||
<main className="mt-8 mb-16 md:mt-16 md:mb-32"> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
ActionButton | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2>Variants</h2> | |||||
<div> | |||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
border | |||||
block | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
disabled | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
border | |||||
block | |||||
disabled | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2>Sizes</h2> | |||||
<div> | |||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
border | |||||
size={ButtonSize.MEDIUM} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.MEDIUM} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
border | |||||
size={ButtonSize.LARGE} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.LARGE} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2>Compact</h2> | |||||
<div> | |||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
compact | |||||
border | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
compact | |||||
border | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
compact | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Action.ActionButton> | |||||
</div> | |||||
<div> | |||||
<Action.ActionButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
subtext={ | |||||
<> | |||||
Very Long Line of Subtext That Spans More Than The Component Width For Testing Overflow | |||||
</> | |||||
} | |||||
> | |||||
Very Long Line of Text That Spans More Than The Component Width For Testing Overflow | |||||
</Action.ActionButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</main> | |||||
</DefaultLayout> | |||||
) | |||||
} | |||||
export default ActionPage |
@@ -0,0 +1,45 @@ | |||||
import { NextPage } from 'next'; | |||||
import { DefaultLayout } from 'src/components/DefaultLayout'; | |||||
const CodePage: NextPage = () => { | |||||
return ( | |||||
<DefaultLayout | |||||
title="Code" | |||||
> | |||||
<main className="mt-8 mb-16 md:mt-16 md:mb-32"> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h2> | |||||
VerifyCodeInput | |||||
</h2> | |||||
<div> | |||||
TODO | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h2> | |||||
CodeBlock | |||||
</h2> | |||||
<div> | |||||
TODO | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h2> | |||||
CodeInput | |||||
</h2> | |||||
<div> | |||||
TODO | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</main> | |||||
</DefaultLayout> | |||||
) | |||||
} | |||||
export default CodePage; |
@@ -0,0 +1,666 @@ | |||||
import { NextPage } from 'next'; | |||||
import { TextControlSize, TextControlStyle } from '@tesseract-design/web-base-textcontrol'; | |||||
import * as Freeform from '@tesseract-design/web-freeform-react'; | |||||
import { DefaultLayout } from 'src/components/DefaultLayout'; | |||||
const FreeformPage: NextPage = () => { | |||||
return ( | |||||
<DefaultLayout | |||||
title="Freeform" | |||||
> | |||||
<main className="mt-8 mb-16 md:mt-16 md:mb-32"> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
TextInput | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2> | |||||
Default | |||||
</h2> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Freeform.TextInput | |||||
size={TextControlSize.SMALL} | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
border | |||||
size={TextControlSize.SMALL} | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
border | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
size={TextControlSize.LARGE} | |||||
label="TextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
border | |||||
size={TextControlSize.LARGE} | |||||
label="TextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
label="TextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
border | |||||
label="TextInput" | |||||
hint="Type anything here…" | |||||
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={TextControlSize.SMALL} | |||||
/> | |||||
{' '} | |||||
but you can call me | |||||
{' '} | |||||
<Freeform.TextInput | |||||
border | |||||
label="Nickname" | |||||
size={TextControlSize.SMALL} | |||||
/> | |||||
. | |||||
</div> | |||||
</form> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2>Alternate</h2> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Freeform.TextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="TextInput ffgg" | |||||
hint="Type anything here… ffgg" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
border | |||||
block | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="TextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="TextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.TextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
border | |||||
label="TextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
MaskedTextInput | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2> | |||||
Default | |||||
</h2> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
size={TextControlSize.SMALL} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
border | |||||
size={TextControlSize.SMALL} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
border | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
size={TextControlSize.LARGE} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
border | |||||
size={TextControlSize.LARGE} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
border | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2>Alternate</h2> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
border | |||||
block | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MaskedTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
border | |||||
label="MaskedTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
MultilineTextInput | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2> | |||||
Default | |||||
</h2> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
size={TextControlSize.SMALL} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
border | |||||
size={TextControlSize.SMALL} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
border | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
size={TextControlSize.LARGE} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
border | |||||
size={TextControlSize.LARGE} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
border | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2>Alternate</h2> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
border | |||||
block | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Freeform.MultilineTextInput | |||||
style={TextControlStyle.ALTERNATE} | |||||
border | |||||
label="MultilineTextInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</main> | |||||
</DefaultLayout> | |||||
); | |||||
}; | |||||
export default FreeformPage; |
@@ -0,0 +1,480 @@ | |||||
import { NextPage } from 'next'; | |||||
import { ButtonSize, ButtonVariant } from '@tesseract-design/web-base-button'; | |||||
import * as Navigation from '@tesseract-design/web-navigation-react'; | |||||
import * as Info from '@tesseract-design/web-info-react'; | |||||
const ActionPage: NextPage = () => { | |||||
return ( | |||||
<main className="my-16 md:my-32"> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
Navigation | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2> | |||||
LinkButton | |||||
</h2> | |||||
<div> | |||||
<section> | |||||
<h3>Variants</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
border | |||||
block | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
disabled | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
border | |||||
block | |||||
disabled | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Sizes</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
size={ButtonSize.MEDIUM} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.MEDIUM} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
size={ButtonSize.LARGE} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.LARGE} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Compact</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
compact | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
compact | |||||
size={ButtonSize.SMALL} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
compact | |||||
size={ButtonSize.LARGE} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.LARGE} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
compact | |||||
menuItem | |||||
size={ButtonSize.SMALL} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
variant={ButtonVariant.FILLED} | |||||
menuItem | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
compact | |||||
menuItem | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
menuItem | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
compact | |||||
menuItem | |||||
size={ButtonSize.LARGE} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Button | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
<div> | |||||
<Navigation.LinkButton | |||||
block | |||||
compact | |||||
border | |||||
size={ButtonSize.LARGE} | |||||
variant={ButtonVariant.FILLED} | |||||
menuItem | |||||
subtext={ | |||||
<> | |||||
Very Long Line of Subtext That Spans More Than The Component Width For Testing Overflow | |||||
</> | |||||
} | |||||
badge={ | |||||
<Info.Badge | |||||
rounded | |||||
> | |||||
69 | |||||
</Info.Badge> | |||||
} | |||||
href="#" | |||||
> | |||||
Very Long Line of Text That Spans More Than The Component Width For Testing Overflow | |||||
</Navigation.LinkButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</main> | |||||
) | |||||
} | |||||
export default ActionPage |
@@ -0,0 +1,51 @@ | |||||
import { NextPage } from 'next'; | |||||
const NumberPage: NextPage = () => { | |||||
return ( | |||||
<main className="my-16 md:my-32"> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
Number | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2> | |||||
Spinner | |||||
</h2> | |||||
<div> | |||||
TODO | |||||
<input type="number" /> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2> | |||||
Slider | |||||
</h2> | |||||
<div> | |||||
TODO | |||||
<input type="range" /> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2> | |||||
Matrix | |||||
</h2> | |||||
<div> | |||||
TODO | |||||
<input type="range" /> | |||||
<input type="range" /> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</main> | |||||
) | |||||
} | |||||
export default NumberPage; |
@@ -0,0 +1,953 @@ | |||||
import { NextPage } from 'next'; | |||||
import { TextControlSize, TextControlStyle } from '@tesseract-design/web-base-textcontrol'; | |||||
import * as Option from '@tesseract-design/web-option-react'; | |||||
import { CheckControlAppearance } from '@tesseract-design/web-base-checkcontrol'; | |||||
import { ButtonSize, ButtonVariant } from '@tesseract-design/web-base-button'; | |||||
type Props = { | |||||
options: Option.SelectOption[], | |||||
} | |||||
const OptionPage: NextPage<Props> = ({ | |||||
options = [ | |||||
{ | |||||
label: 'Fruits', | |||||
children: [ | |||||
{ | |||||
label: 'Mango', | |||||
value: 'mango', | |||||
}, | |||||
{ | |||||
label: 'Strawberry', | |||||
value: 'strawberry', | |||||
}, | |||||
{ | |||||
label: 'Avocado', | |||||
value: 'avocado', | |||||
}, | |||||
{ | |||||
label: 'Grapes', | |||||
value: 'grapes', | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
label: 'Non-fruits', | |||||
children: [ | |||||
{ | |||||
label: 'Chocolate', | |||||
value: 'chocolate', | |||||
}, | |||||
{ | |||||
label: 'Milk', | |||||
value: 'milk', | |||||
}, | |||||
{ | |||||
label: 'Coffee', | |||||
value: 'coffee', | |||||
} | |||||
], | |||||
}, | |||||
], | |||||
}) => { | |||||
return ( | |||||
<main className="my-16 md:my-32"> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
Option | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2> | |||||
DropdownSelect | |||||
</h2> | |||||
<div> | |||||
<section> | |||||
<h3> | |||||
Default | |||||
</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
size={TextControlSize.SMALL} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
border | |||||
size={TextControlSize.SMALL} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
border | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
size={TextControlSize.LARGE} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
border | |||||
size={TextControlSize.LARGE} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
disabled | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
border | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
disabled | |||||
options={options} | |||||
/> | |||||
</div> | |||||
</div> | |||||
<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"> | |||||
<Option.DropdownSelect | |||||
label="First Name" | |||||
block | |||||
border | |||||
/> | |||||
</div> | |||||
<div className="col-span-2"> | |||||
<Option.DropdownSelect | |||||
label="Middle Name" | |||||
block | |||||
border | |||||
/> | |||||
</div> | |||||
<div className="col-span-2"> | |||||
<Option.DropdownSelect | |||||
label="Last Name" | |||||
block | |||||
border | |||||
/> | |||||
</div> | |||||
<div className="col-span-3"> | |||||
<Option.DropdownSelect | |||||
label="Username" | |||||
block | |||||
border | |||||
/> | |||||
</div> | |||||
<div className="col-span-3"> | |||||
<Option.DropdownSelect | |||||
label="Title" | |||||
block | |||||
border | |||||
/> | |||||
</div> | |||||
</div> | |||||
</fieldset> | |||||
</form> | |||||
<form> | |||||
<div> | |||||
Hi, my name is | |||||
{' '} | |||||
<Option.DropdownSelect | |||||
border | |||||
label="Name" | |||||
size={TextControlSize.SMALL} | |||||
/> | |||||
{' '} | |||||
but you can call me | |||||
{' '} | |||||
<Option.DropdownSelect | |||||
border | |||||
label="Nickname" | |||||
size={TextControlSize.SMALL} | |||||
/> | |||||
. | |||||
</div> | |||||
</form> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Alternate</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.SMALL} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
border | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
border | |||||
block | |||||
style={TextControlStyle.ALTERNATE} | |||||
size={TextControlSize.LARGE} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
style={TextControlStyle.ALTERNATE} | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
disabled | |||||
options={options} | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.DropdownSelect | |||||
style={TextControlStyle.ALTERNATE} | |||||
border | |||||
label="Select" | |||||
hint="Type anything here…" | |||||
block | |||||
disabled | |||||
options={options} | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2> | |||||
RadioButton | |||||
</h2> | |||||
<div> | |||||
<section> | |||||
<h3>Default</h3> | |||||
<div> | |||||
<div className="grid gap-4 my-4"> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
subtext="Subtext" | |||||
name="RadioButton" | |||||
> | |||||
RadioButton | |||||
</Option.RadioButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Switch</h3> | |||||
<div> | |||||
<div className="grid gap-4 my-4"> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
subtext="Subtext" | |||||
name="RadioButton" | |||||
appearance={CheckControlAppearance.SWITCH} | |||||
> | |||||
RadioButton | |||||
</Option.RadioButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Button</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
border | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
border | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Sizes</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
border | |||||
size={ButtonSize.MEDIUM} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.MEDIUM} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
border | |||||
size={ButtonSize.LARGE} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.LARGE} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Compact</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
compact | |||||
border | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
compact | |||||
border | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
compact | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
<div> | |||||
<Option.RadioButton | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
name="RadioButton" | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.RadioButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2> | |||||
Checkbox | |||||
</h2> | |||||
<div> | |||||
<section> | |||||
<h3>Default</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
subtext="Subtext" | |||||
> | |||||
Checkbox | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
indeterminate | |||||
subtext="Subtext" | |||||
> | |||||
Checkbox | |||||
</Option.Checkbox> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Switch</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
subtext="Subtext" | |||||
appearance={CheckControlAppearance.SWITCH} | |||||
> | |||||
Checkbox | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
indeterminate | |||||
subtext="Subtext" | |||||
appearance={CheckControlAppearance.SWITCH} | |||||
> | |||||
Checkbox | |||||
</Option.Checkbox> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Button</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
border | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
border | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
disabled | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
border | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
indeterminate | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
indeterminate | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Sizes</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
border | |||||
size={ButtonSize.MEDIUM} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.MEDIUM} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
border | |||||
size={ButtonSize.LARGE} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.LARGE} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h3>Compact</h3> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
compact | |||||
border | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
compact | |||||
border | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
compact | |||||
border | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
<div> | |||||
<Option.Checkbox | |||||
block | |||||
compact | |||||
border | |||||
variant={ButtonVariant.FILLED} | |||||
size={ButtonSize.SMALL} | |||||
appearance={CheckControlAppearance.BUTTON} | |||||
subtext={ | |||||
<> | |||||
Subtext | |||||
</> | |||||
} | |||||
> | |||||
Button | |||||
</Option.Checkbox> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2> | |||||
TagList | |||||
</h2> | |||||
<div> | |||||
TODO | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2> | |||||
ComboBox | |||||
</h2> | |||||
<div> | |||||
TODO input with datalist | |||||
</div> | |||||
</section> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</main> | |||||
) | |||||
} | |||||
export default OptionPage |
@@ -0,0 +1,92 @@ | |||||
import { NextPage } from 'next'; | |||||
import * as Freeform from '@tesseract-design/web-freeform-react'; | |||||
import * as Action from '@tesseract-design/web-action-react'; | |||||
import { ButtonVariant } from '@tesseract-design/web-base-button'; | |||||
const RegistrationFormPage: NextPage = () => { | |||||
return ( | |||||
<main className="my-16 md:my-32"> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
Registration Form | |||||
</h1> | |||||
<p> | |||||
Input your data in order to gain access to the functions of this service. | |||||
</p> | |||||
<form> | |||||
<fieldset className="contents"> | |||||
<legend className="sr-only"> | |||||
Register | |||||
</legend> | |||||
<div className="grid sm:grid-cols-6 gap-4"> | |||||
<div className="sm:col-span-2"> | |||||
<Freeform.TextInput | |||||
block | |||||
border | |||||
label="First Name" | |||||
name="firstName" | |||||
/> | |||||
</div> | |||||
<div className="sm:col-span-2"> | |||||
<Freeform.TextInput | |||||
block | |||||
border | |||||
label="Middle Name" | |||||
name="middleName" | |||||
/> | |||||
</div> | |||||
<div className="sm:col-span-2"> | |||||
<Freeform.TextInput | |||||
block | |||||
border | |||||
label="Last Name" | |||||
name="lastName" | |||||
/> | |||||
</div> | |||||
<div className="sm:col-span-3"> | |||||
<Freeform.TextInput | |||||
block | |||||
border | |||||
label="Email" | |||||
name="email" | |||||
/> | |||||
</div> | |||||
<div className="sm:col-span-3"> | |||||
<Freeform.TextInput | |||||
block | |||||
border | |||||
label="Website" | |||||
name="website" | |||||
/> | |||||
</div> | |||||
<div className="sm:col-span-6 text-center"> | |||||
<div | |||||
className="sm:flex items-center justify-end space-y-4 sm:space-y-0 sm:space-x-4" | |||||
> | |||||
<small> | |||||
By registering, you agree to the <a href="#">Terms</a> of this service. | |||||
</small> | |||||
{' '} | |||||
<div> | |||||
<Action.ActionButton | |||||
variant={ButtonVariant.FILLED} | |||||
block | |||||
border | |||||
type={Action.ActionButtonType.SUBMIT} | |||||
menuItem | |||||
subtext="Registration requires activation" | |||||
> | |||||
Submit | |||||
</Action.ActionButton> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</fieldset> | |||||
</form> | |||||
</div> | |||||
</main> | |||||
) | |||||
} | |||||
export default RegistrationFormPage; |
@@ -0,0 +1,111 @@ | |||||
import type { NextPage } from 'next' | |||||
import Link from 'next/link'; | |||||
import * as Navigation from '@tesseract-design/web-navigation-react'; | |||||
import { DefaultLayout } from 'src/components/DefaultLayout'; | |||||
type Page = { | |||||
id: string, | |||||
href: string, | |||||
label: string, | |||||
} | |||||
type Props = { | |||||
componentPages?: Page[], | |||||
examplePages?: Page[], | |||||
} | |||||
const createPageLink = (p: Page) => ( | |||||
<div | |||||
key={p.id} | |||||
> | |||||
<Link | |||||
href={p.href} | |||||
passHref | |||||
> | |||||
<Navigation.LinkButton | |||||
block | |||||
border | |||||
menuItem | |||||
> | |||||
{p.label} | |||||
</Navigation.LinkButton> | |||||
</Link> | |||||
</div> | |||||
) | |||||
const IndexPage: NextPage<Props> = ({ | |||||
componentPages = [ | |||||
{ | |||||
id: 'action', | |||||
href: '/categories/action', | |||||
label: 'Action', | |||||
}, | |||||
{ | |||||
id: 'code', | |||||
href: '/categories/code', | |||||
label: 'Code', | |||||
}, | |||||
{ | |||||
id: 'freeform', | |||||
href: '/categories/freeform', | |||||
label: 'Freeform', | |||||
}, | |||||
{ | |||||
id: 'navigation', | |||||
href: '/categories/navigation', | |||||
label: 'Navigation', | |||||
}, | |||||
{ | |||||
id: 'number', | |||||
href: '/categories/number', | |||||
label: 'Number', | |||||
}, | |||||
{ | |||||
id: 'option', | |||||
href: '/categories/option', | |||||
label: 'Option', | |||||
}, | |||||
], | |||||
examplePages = [ | |||||
{ | |||||
id: 'registration-form', | |||||
href: '/examples/registration-form', | |||||
label: 'Registration Form', | |||||
} | |||||
], | |||||
}) => { | |||||
return ( | |||||
<DefaultLayout | |||||
title="Kitchen Sink" | |||||
> | |||||
<main className="mt-8 mb-16 md:mt-16 md:mb-32"> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
Components | |||||
</h1> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
{componentPages.map(createPageLink)} | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
Examples | |||||
</h1> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||||
{examplePages.map(createPageLink)} | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
</main> | |||||
</DefaultLayout> | |||||
) | |||||
} | |||||
export default IndexPage |
@@ -0,0 +1,35 @@ | |||||
{ | |||||
"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", | |||||
"baseUrl": ".", | |||||
"paths": { | |||||
"@tesseract-design/web-action-react": ["./src/modules/action"], | |||||
"@tesseract-design/web-freeform-react": ["./src/modules/freeform"], | |||||
"@tesseract-design/web-info-react": ["./src/modules/info"], | |||||
"@tesseract-design/web-navigation-react": ["./src/modules/navigation"], | |||||
"@tesseract-design/web-option-react": ["./src/modules/option"], | |||||
"@tesseract-design/web-base-button": ["./src/modules/base-button"], | |||||
"@tesseract-design/web-base-checkcontrol": ["./src/modules/base-checkcontrol"], | |||||
"@tesseract-design/web-base-textcontrol": ["./src/modules/base-textcontrol"], | |||||
"@tesseract-design/css-utils": ["./src/modules/css-utils"] | |||||
} | |||||
}, | |||||
"include": [ | |||||
"next-env.d.ts", | |||||
"**/*.ts", | |||||
"**/*.tsx" | |||||
], | |||||
"exclude": ["node_modules", "./src/packages"] | |||||
} |
@@ -0,0 +1,3 @@ | |||||
{ | |||||
"extends": "./tsconfig.json" | |||||
} |