Reviewed-on: https://code.modal.sh/tesseract-design/tesseract/pulls/3master
@@ -0,0 +1,60 @@ | |||
# Categories | |||
- Action | |||
- [X] ActionButton | |||
- Blob (choose to extract each preview component to their own library?) | |||
- [ ] FileSelectBox | |||
- Choice | |||
- [X] ComboBox | |||
- [X] DropdownSelect | |||
- [X] MenuSelect | |||
- [X] RadioButton | |||
- [X] RadioTickBox | |||
- Color | |||
- [ ] ColorPicker | |||
- [X] Swatch | |||
- Formatted | |||
- [X] EmailInput | |||
- [X] PhoneNumberInput | |||
- [X] UrlInput | |||
- Freeform | |||
- [X] MaskedTextInput | |||
- [X] MultilineTextInput | |||
- [X] TextInput | |||
- Geo | |||
- [ ] Map | |||
- Information | |||
- [X] Badge | |||
- [X] KeyValueTable | |||
- MultiChoice | |||
- [X] MenuMultiSelect | |||
- [X] TagInput | |||
- [X] ToggleButton | |||
- [X] ToggleSwitch | |||
- [X] ToggleTickBox | |||
- Navigation | |||
- [ ] Breadcrumb | |||
- [X] LinkButton | |||
- [ ] Pagination | |||
- [ ] Steps | |||
- Number | |||
- [X] Slider | |||
- [X] Spinner | |||
- [ ] Matrix2D | |||
- Rating | |||
- [ ] Rating | |||
- RichText | |||
- [ ] RichTextInput | |||
- Temporal | |||
- [ ] Calendar | |||
- [ ] DateDropdown | |||
- [ ] DateTimeRangeInput | |||
- [ ] MonthInput | |||
- [ ] MonthDayInput | |||
- [ ] TimeSpinner | |||
- [ ] YearMonthInput | |||
- [ ] YearWeekInput | |||
- [ ] YearInput | |||
# Others | |||
- [ ] Add `select-none` to input labels, etc. |
@@ -0,0 +1,9 @@ | |||
{ | |||
"root": true, | |||
"extends": [ | |||
"lxsmnsyc/typescript" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -1,27 +1,26 @@ | |||
{ | |||
"name": "@tesseract-design/web-base-selectcontrol", | |||
"name": "@tesseract-design/web-base", | |||
"version": "0.0.0", | |||
"files": [ | |||
"dist", | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=10" | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"devDependencies": { | |||
"@types/node": "^18.0.0", | |||
"eslint": "^8.20.0", | |||
"eslint-config-lxsmnsyc": "^0.4.7", | |||
"@types/node": "^18.14.1", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"pridepack": "2.4.4", | |||
"tslib": "^2.4.0", | |||
"typescript": "^4.7.4", | |||
"vitest": "^0.19.1" | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"dependencies": {}, | |||
"scripts": { | |||
"prepublishOnly": "pridepack clean && pridepack build", | |||
"build": "pridepack build", | |||
@@ -33,8 +32,8 @@ | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": true, | |||
"description": "Base select control styles for Tesseract.", | |||
"private": false, | |||
"description": "Base types for Tesseract.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
@@ -45,9 +44,11 @@ | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "restricted" | |||
"access": "public" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
@@ -61,7 +62,5 @@ | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
}, | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js" | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,5 @@ | |||
export type Type = 'submit' | 'reset' | 'button'; | |||
export type Variant = 'bare' | 'filled' | 'outline'; | |||
export type Size = 'small' | 'medium' | 'large'; |
@@ -0,0 +1,2 @@ | |||
export * as Button from './button'; | |||
export * as TextControl from './text-control'; |
@@ -0,0 +1,7 @@ | |||
export type Size = 'small' | 'medium' | 'large'; | |||
export type Variant = 'default' | 'alternate'; | |||
export type InputType = 'text' | 'search'; | |||
export type InputMode = 'none' | 'numeric' | 'decimal' | InputType; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
{ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off", | |||
"react/button-has-type": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -6,32 +6,27 @@ | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=10" | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"dependencies": { | |||
"@tesseract-design/web-base-button": "link:../../../base/button" | |||
}, | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.4", | |||
"@testing-library/react": "^13.3.0", | |||
"@testing-library/react-hooks": "^8.0.1", | |||
"@testing-library/user-event": "^13.5.0", | |||
"@types/node": "^18.0.0", | |||
"@types/react": "^18.0.14", | |||
"eslint": "^8.20.0", | |||
"eslint-config-lxsmnsyc": "^0.4.7", | |||
"jsdom": "^20.0.0", | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.4.0", | |||
"typescript": "^4.7.4", | |||
"vitest": "^0.31.0" | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -48,8 +43,8 @@ | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": true, | |||
"description": "Action components for Tesseract for use in React.", | |||
"private": false, | |||
"description": "Tesseract components for committing actions for forms.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
@@ -60,9 +55,15 @@ | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "restricted" | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"clsx": "^1.2.1", | |||
"@tesseract-design/web-base": "workspace:*" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
@@ -76,7 +77,5 @@ | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
}, | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js" | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,134 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
import { Button } from '@tesseract-design/web-base'; | |||
export type ActionButtonDerivedElement = HTMLButtonElement; | |||
export interface ActionButtonProps extends Omit<React.HTMLProps<ActionButtonDerivedElement>, 'type' | 'size'> { | |||
type?: Button.Type; | |||
variant?: Button.Variant; | |||
block?: boolean; | |||
subtext?: React.ReactNode; | |||
badge?: React.ReactNode; | |||
menuItem?: boolean; | |||
size?: Button.Size; | |||
compact?: boolean; | |||
} | |||
export const ActionButton = React.forwardRef<ActionButtonDerivedElement, ActionButtonProps>(( | |||
{ | |||
type = 'button' as const, | |||
variant = 'bare' as const, | |||
subtext, | |||
badge, | |||
menuItem = false, | |||
children, | |||
size = 'medium' as const, | |||
compact = false, | |||
className, | |||
block = false, | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => ( | |||
<button | |||
{...etcProps} | |||
type={type} | |||
ref={forwardedRef} | |||
className={clsx( | |||
'items-center justify-center rounded overflow-hidden ring-secondary/50 leading-none select-none', | |||
'focus:outline-0 focus:ring-4', | |||
'active:ring-tertiary/50', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'flex w-full': block, | |||
'inline-flex max-w-full align-middle': !block, | |||
}, | |||
{ | |||
'pl-2 gap-2': compact, | |||
'pl-4 gap-4': !compact, | |||
'pr-4': !(compact || menuItem), | |||
'pr-2': compact || menuItem, | |||
}, | |||
{ | |||
'border-2 border-primary disabled:border-primary focus:border-secondary active:border-tertiary': variant !== 'bare', | |||
'bg-negative text-primary disabled:text-primary focus:text-secondary active:text-tertiary': variant !== 'filled', | |||
'bg-primary text-negative disabled:bg-primary focus:bg-secondary active:bg-tertiary': variant === 'filled', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
className, | |||
)} | |||
> | |||
<span | |||
className={clsx( | |||
'flex-auto min-w-0', | |||
{ | |||
'text-left': compact || menuItem, | |||
'text-center': !(compact || menuItem), | |||
}, | |||
)} | |||
> | |||
<span | |||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||
data-testid="children" | |||
> | |||
{children} | |||
</span> | |||
{subtext && ( | |||
<> | |||
<span className="sr-only"> | |||
{' - '} | |||
</span> | |||
<span | |||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||
data-testid="subtext" | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
)} | |||
</span> | |||
{badge && ( | |||
<> | |||
<span className="sr-only"> | |||
{' - '} | |||
</span> | |||
<span | |||
data-testid="badge" | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
)} | |||
{menuItem && ( | |||
<span | |||
data-testid="menuItemIndicator" | |||
> | |||
<svg | |||
className="w-6 h-6 fill-none stroke-current stroke-2 linejoin-round linecap-round" | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline points="9 18 15 12 9 6" /> | |||
</svg> | |||
</span> | |||
)} | |||
</button> | |||
)); | |||
ActionButton.displayName = 'ActionButton'; | |||
ActionButton.defaultProps = { | |||
type: 'button', | |||
variant: 'bare', | |||
block: false, | |||
subtext: undefined, | |||
badge: undefined, | |||
menuItem: false, | |||
size: 'medium', | |||
compact: false, | |||
}; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
/// <reference types="vitest" /> | |||
export default ({ | |||
test: { | |||
global: true, | |||
environment: 'jsdom', | |||
}, | |||
}); |
@@ -0,0 +1,22 @@ | |||
import PackageDescription | |||
let package = Package( | |||
name: "Action", | |||
platforms: [ | |||
.macOS(.v11), | |||
.iOS(.v13) | |||
], | |||
products: [ | |||
.library( | |||
name: "Action", | |||
targets: ["Action"]), | |||
], | |||
dependencies: [], | |||
targets: [ | |||
.target( | |||
name: "Action", | |||
dependencies: [], | |||
path: "src", | |||
), | |||
] | |||
) |
@@ -0,0 +1,4 @@ | |||
import SwiftUI | |||
public struct ActionButton: View { | |||
} |
@@ -0,0 +1,9 @@ | |||
{ | |||
"root": true, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -0,0 +1,81 @@ | |||
{ | |||
"name": "@tesseract-design/web-blob-react", | |||
"version": "0.0.0", | |||
"files": [ | |||
"dist", | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
"react-dom": "^16.8 || ^17.0 || ^18.0" | |||
}, | |||
"scripts": { | |||
"prepublishOnly": "pridepack clean && pridepack build", | |||
"build": "pridepack build", | |||
"type-check": "pridepack check", | |||
"lint": "pridepack lint", | |||
"clean": "pridepack clean", | |||
"watch": "pridepack watch", | |||
"start": "pridepack start", | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": false, | |||
"description": "Tesseract components for displaying data.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
}, | |||
"homepage": "", | |||
"bugs": { | |||
"url": "" | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"clsx": "^1.2.1", | |||
"@modal-sh/react-utils": "workspace:*" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
"require": "./dist/cjs/development/index.js", | |||
"import": "./dist/esm/development/index.js" | |||
}, | |||
"require": "./dist/cjs/production/index.js", | |||
"import": "./dist/esm/production/index.js", | |||
"types": "./dist/types/index.d.ts" | |||
} | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,300 @@ | |||
import * as React from 'react'; | |||
import { delegateTriggerEvent, useClientSide } from '@modal-sh/react-utils'; | |||
import clsx from 'clsx'; | |||
export interface CommonPreviewProps<F extends Partial<File> = Partial<File>> { | |||
file?: F; | |||
disabled?: boolean; | |||
enhanced?: boolean; | |||
mini?: boolean; | |||
} | |||
export type FileSelectBoxDerivedElement = HTMLInputElement; | |||
export interface FileSelectBoxProps< | |||
F extends Partial<File> = Partial<File>, | |||
P extends CommonPreviewProps<F> = CommonPreviewProps<F> | |||
> extends Omit<React.HTMLProps<FileSelectBoxDerivedElement>, 'size' | 'type' | 'style' | 'label' | 'list'> { | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* 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, | |||
enhanced?: boolean, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
previewComponent: React.ElementType<P>, | |||
} | |||
export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileSelectBoxProps>( | |||
( | |||
{ | |||
label = '', | |||
hint = '', | |||
border = false, | |||
block = false, | |||
enhanced: enhancedProp = false, | |||
hiddenLabel = false, | |||
multiple = false, | |||
onChange, | |||
disabled = false, | |||
className, | |||
id: idProp, | |||
previewComponent: FilePreviewComponent, | |||
...etcProps | |||
}: FileSelectBoxProps, | |||
forwardedRef, | |||
) => { | |||
const { clientSide } = useClientSide({ clientSide: enhancedProp }); | |||
const [fileList, setFileList] = React.useState<FileList>(); | |||
const [lastUpdated, setLastUpdated] = React.useState<number>(); | |||
const defaultRef = React.useRef<HTMLInputElement>(null); | |||
const ref = forwardedRef ?? defaultRef; | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
const doSetFileList: React.ChangeEventHandler<HTMLInputElement> = (e) => { | |||
if (enhancedProp) { | |||
setFileList(e.currentTarget.files as FileList); | |||
setLastUpdated(Date.now()); | |||
} | |||
onChange?.(e); | |||
}; | |||
const doClearFileList: React.MouseEventHandler<HTMLButtonElement> = (e) => { | |||
e.preventDefault(); | |||
if (!(typeof ref === 'object' && ref)) { | |||
return; | |||
} | |||
const { current } = ref; | |||
if (!current) { | |||
return; | |||
} | |||
current.value = ''; | |||
setFileList(undefined); | |||
setLastUpdated(Date.now()); | |||
setTimeout(() => { | |||
delegateTriggerEvent('change', current); | |||
}); | |||
}; | |||
const cancelEvent = (e: React.DragEvent) => { | |||
e.stopPropagation(); | |||
e.preventDefault(); | |||
}; | |||
const handleDropZone: React.DragEventHandler<HTMLDivElement> = async (e) => { | |||
cancelEvent(e); | |||
if (!(typeof ref === 'object' && ref)) { | |||
return; | |||
} | |||
const { current } = ref; | |||
if (!current) { | |||
return; | |||
} | |||
const { dataTransfer } = e; | |||
const { files } = dataTransfer; | |||
if (!(files && files.length > 0)) { | |||
return; | |||
} | |||
setFileList(current.files = files); | |||
setLastUpdated(Date.now()); | |||
setTimeout(() => { | |||
delegateTriggerEvent('change', current); | |||
}); | |||
}; | |||
const filesCount = fileList?.length ?? 0; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50 group', | |||
'focus-within:ring-4', | |||
block && 'w-full', | |||
!block && 'inline-block min-w-64', | |||
className, | |||
)} | |||
onDragEnter={cancelEvent} | |||
onDragOver={cancelEvent} | |||
onDrop={handleDropZone} | |||
data-testid="root" | |||
> | |||
<label | |||
className="block absolute top-0 left-0 w-full h-full cursor-pointer" | |||
data-testid="clickArea" | |||
htmlFor={id} | |||
/> | |||
<input | |||
{...etcProps} | |||
id={id} | |||
disabled={disabled} | |||
ref={ref} | |||
type="file" | |||
className={clsx( | |||
'peer', | |||
{ | |||
'sr-only': clientSide, | |||
} | |||
)} | |||
onChange={doSetFileList} | |||
multiple={multiple} | |||
data-testid="input" | |||
aria-labelledby={label ? `${labelId}` : undefined} | |||
/> | |||
{ | |||
label && ( | |||
<div | |||
data-testid="label" | |||
id={labelId} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 group-focus-within:text-secondary text-primary leading-none bg-negative', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
)} | |||
> | |||
<div className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</div> | |||
</div> | |||
) | |||
} | |||
{ | |||
filesCount < 1 | |||
&& clientSide | |||
&& hint | |||
&& ( | |||
<div | |||
data-testid="hint" | |||
className="absolute top-0 left-0 w-full h-full pointer-events-none box-border overflow-hidden pt-4" | |||
> | |||
<div className="flex items-center justify-center w-full h-full"> | |||
{hint} | |||
</div> | |||
</div> | |||
) | |||
} | |||
{ | |||
filesCount > 0 | |||
&& clientSide | |||
&& ( | |||
<React.Fragment key={lastUpdated}> | |||
<div className={`sm:absolute top-0 left-0 w-full h-full pointer-events-none pb-12 box-border overflow-hidden pt-8`}> | |||
<div | |||
className={`pointer-events-auto w-full h-full px-4 pb-4 box-border`} | |||
> | |||
{ | |||
multiple | |||
&& ( | |||
<div className="w-full h-full overflow-auto -mx-4 px-4"> | |||
<div className="w-full grid gap-4 grid-cols-3"> | |||
{Array.from(fileList ?? []).map((file, i) => { | |||
return ( | |||
<div | |||
key={`${file.name}:${i}`} | |||
data-testid="selectedFileItem" | |||
className={`w-full aspect-square rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10`} | |||
> | |||
<FilePreviewComponent | |||
file={file} | |||
enhanced={clientSide} | |||
disabled={disabled} | |||
mini | |||
/> | |||
</div> | |||
); | |||
})} | |||
</div> | |||
</div> | |||
) | |||
} | |||
{ | |||
!multiple | |||
&& Array.from(fileList ?? []).map((file, i) => { | |||
return ( | |||
<div | |||
key={`${file.name}:${i}`} | |||
className="w-full h-full" | |||
> | |||
<div | |||
data-testid="selectedFileItem" | |||
className="h-full w-full p-4 box-border rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10" | |||
> | |||
<div | |||
className="w-full h-full relative" | |||
> | |||
<FilePreviewComponent | |||
file={file} | |||
enhanced={clientSide} | |||
disabled={disabled} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
) | |||
}) | |||
} | |||
</div> | |||
</div> | |||
<div className="absolute bottom-0 left-0 w-full text-center h-12 box-border flex"> | |||
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full"> | |||
<label | |||
data-testid="reselect" | |||
htmlFor={id} | |||
className="flex w-full h-full bg-negative text-primary disabled:text-primary focus:text-secondary active:text-tertiary items-center justify-center leading-none gap-4 select-none" | |||
> | |||
<span | |||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||
> | |||
Reselect | |||
</span> | |||
</label> | |||
</div> | |||
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full"> | |||
<button | |||
data-testid="clear" | |||
type="button" | |||
onClick={doClearFileList} | |||
className="flex w-full h-full bg-negative text-primary disabled:text-primary focus:text-secondary active:text-tertiary items-center justify-center leading-none gap-4 select-none focus:outline-0" | |||
> | |||
<span | |||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||
> | |||
Clear | |||
</span> | |||
</button> | |||
</div> | |||
</div> | |||
</React.Fragment> | |||
) | |||
} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none group-focus-within:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
} | |||
); | |||
FileSelectBox.displayName = 'FileSelectBox'; |
@@ -0,0 +1 @@ | |||
export * from './components/FileSelectBox'; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
/// <reference types="vitest" /> | |||
export default ({ | |||
test: { | |||
global: true, | |||
environment: 'jsdom', | |||
}, | |||
}); |
@@ -0,0 +1,13 @@ | |||
{ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -0,0 +1,84 @@ | |||
{ | |||
"name": "@tesseract-design/web-choice-react", | |||
"version": "0.0.0", | |||
"files": [ | |||
"dist", | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.5.0", | |||
"tsx": "^3.12.7", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
"react-dom": "^16.8 || ^17.0 || ^18.0" | |||
}, | |||
"scripts": { | |||
"prepublishOnly": "pridepack clean && pridepack build", | |||
"build": "pridepack build && tsx scripts/build.ts", | |||
"type-check": "pridepack check", | |||
"lint": "pridepack lint", | |||
"clean": "pridepack clean", | |||
"watch": "pridepack watch", | |||
"start": "pridepack start", | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": false, | |||
"description": "Tesseract components for selecting a single value across an array of options.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
}, | |||
"homepage": "", | |||
"bugs": { | |||
"url": "" | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"@tesseract-design/web-base": "workspace:*", | |||
"clsx": "^1.2.1" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
"require": "./dist/cjs/development/index.js", | |||
"import": "./dist/esm/development/index.js" | |||
}, | |||
"require": "./dist/cjs/production/index.js", | |||
"import": "./dist/esm/production/index.js", | |||
"types": "./dist/types/index.d.ts" | |||
}, | |||
"./dist/RadioButton.css": "./dist/RadioButton.css", | |||
"./dist/RadioTickBox.css": "./dist/RadioTickBox.css" | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,14 @@ | |||
import { copyFileSync } from 'fs'; | |||
import { resolve } from 'path'; | |||
const doCopy = (src: string, dest: string) => { | |||
const trueSrc = resolve(src); | |||
const trueDest = resolve(dest); | |||
console.log('Copying...'); | |||
console.log(`${trueSrc} -> ${trueDest}`); | |||
copyFileSync(trueSrc, trueDest); | |||
console.log('Done'); | |||
} | |||
doCopy('./src/components/RadioButton/RadioButton.css', './dist/RadioButton.css'); | |||
doCopy('./src/components/RadioTickBox/RadioTickBox.css', './dist/RadioTickBox.css'); |
@@ -0,0 +1,242 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type ComboBoxDerivedElement = HTMLInputElement; | |||
export interface ComboBoxProps extends Omit<React.HTMLProps<ComboBoxDerivedElement>, 'size' | 'type' | 'style' | 'label' | 'list' | 'inputMode'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* 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?: TextControl.InputType, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
/** | |||
* Input mode of the component. | |||
*/ | |||
inputMode?: TextControl.InputMode, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
* | |||
* This component supports multiline input and adjusts its layout accordingly. | |||
*/ | |||
export const ComboBox = React.forwardRef<ComboBoxDerivedElement, ComboBoxProps>(( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
type = 'text' as const, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
children, | |||
inputMode = 'text' as const, | |||
id: idProp, | |||
...etcProps | |||
}: ComboBoxProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const datalistId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
let resultInputMode = inputMode as React.HTMLProps<ComboBoxDerivedElement>['inputMode']; | |||
if (type === 'text' && resultInputMode === 'search') { | |||
resultInputMode = 'text'; | |||
} else if (type === 'search' && resultInputMode === 'text') { | |||
resultInputMode = 'search'; | |||
} | |||
return ( | |||
<> | |||
<datalist | |||
id={datalistId} | |||
> | |||
{children} | |||
</datalist> | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
aria-labelledby={labelId} | |||
type={type} | |||
inputMode={resultInputMode} | |||
list={datalistId} | |||
data-testid="input" | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
id={labelId} | |||
htmlFor={id} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
</> | |||
); | |||
}); | |||
ComboBox.displayName = 'ComboBox'; | |||
ComboBox.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
indicator: undefined, | |||
size: 'medium', | |||
border: false, | |||
block: false, | |||
type: 'text', | |||
variant: 'default', | |||
hiddenLabel: false, | |||
inputMode: 'text', | |||
}; |
@@ -0,0 +1,216 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type DropdownSelectDerivedElement = HTMLSelectElement; | |||
export interface DropdownSelectProps extends Omit<React.HTMLProps<DropdownSelectDerivedElement>, 'size' | 'type' | 'style' | 'label' | 'list' | 'multiple'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* Additional description, usually graphical, indicating the nature of the component's value. | |||
*/ | |||
indicator?: React.ReactNode, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
* | |||
* This component supports multiline input and adjusts its layout accordingly. | |||
*/ | |||
export const DropdownSelect = React.forwardRef<DropdownSelectDerivedElement, DropdownSelectProps>( | |||
( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
children, | |||
id: idProp, | |||
...etcProps | |||
}: DropdownSelectProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50 min-w-48', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
> | |||
<select | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
aria-labelledby={labelId} | |||
data-testid="input" | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block appearance-none', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{children} | |||
</select> | |||
{ | |||
label && ( | |||
<label | |||
htmlFor={id} | |||
data-testid="label" | |||
id={labelId} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}, | |||
); | |||
DropdownSelect.displayName = 'DropdownSelect'; | |||
DropdownSelect.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
indicator: undefined, | |||
size: 'medium', | |||
border: false, | |||
block: false, | |||
variant: 'default', | |||
hiddenLabel: false, | |||
}; |
@@ -0,0 +1,234 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type MenuSelectDerivedElement = HTMLSelectElement; | |||
export interface MenuSelectProps extends Omit<React.HTMLProps<MenuSelectDerivedElement>, 'size' | 'style' | 'label' | 'multiple'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* Additional description, usually graphical, indicating the nature of the component's value. | |||
*/ | |||
indicator?: React.ReactNode, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
/** | |||
* Starting height of the component. | |||
*/ | |||
startingHeight?: number | string, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
* | |||
* This component supports multiline input and adjusts its layout accordingly. | |||
*/ | |||
export const MenuSelect = React.forwardRef<MenuSelectDerivedElement, MenuSelectProps>( | |||
( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
startingHeight = '15rem', | |||
id: idProp, | |||
...etcProps | |||
}: MenuSelectProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
> | |||
<select | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
aria-labelledby={labelId} | |||
data-testid="input" | |||
size={2} | |||
style={{ | |||
height: startingHeight, | |||
}} | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block overflow-auto', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'resize': !block, | |||
'resize-y': block, | |||
}, | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate' && size === 'small', | |||
'pt-5': variant === 'alternate' && size === 'medium', | |||
'pt-8': variant === 'alternate' && size === 'large', | |||
}, | |||
{ | |||
'py-2.5': variant === 'default' && size === 'small', | |||
'py-3': variant === 'default' && size === 'medium', | |||
'py-5': variant === 'default' && size === 'large', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'min-h-10': size === 'small', | |||
'min-h-12': size === 'medium', | |||
'min-h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
htmlFor={id} | |||
id={labelId} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}, | |||
); | |||
MenuSelect.displayName = 'MenuSelect'; | |||
MenuSelect.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
indicator: undefined, | |||
size: 'medium', | |||
border: false, | |||
block: false, | |||
variant: 'default', | |||
hiddenLabel: false, | |||
startingHeight: '15rem', | |||
}; |
@@ -0,0 +1,7 @@ | |||
.tesseract-design-radio-button + label > :first-child > :first-child { | |||
display: none; | |||
} | |||
.tesseract-design-radio-button:checked + label > :first-child > :first-child { | |||
display: block; | |||
} |
@@ -0,0 +1,153 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
import { Button } from '@tesseract-design/web-base'; | |||
export type RadioButtonDerivedElement = HTMLInputElement; | |||
export interface RadioButtonProps extends Omit<React.InputHTMLAttributes<RadioButtonDerivedElement>, 'type' | 'size'> { | |||
block?: boolean; | |||
compact?: boolean; | |||
size?: Button.Size; | |||
subtext?: React.ReactNode; | |||
badge?: React.ReactNode; | |||
variant?: Button.Variant; | |||
} | |||
export const RadioButton = React.forwardRef<RadioButtonDerivedElement, RadioButtonProps>(( | |||
{ | |||
children, | |||
block = false, | |||
compact = false, | |||
size = 'medium' as const, | |||
id: idProp, | |||
className, | |||
subtext, | |||
badge, | |||
variant = 'bare' as const, | |||
style, | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => { | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
type="radio" | |||
id={id} | |||
className="sr-only peer tesseract-design-radio-button" | |||
/> | |||
<label | |||
style={style} | |||
htmlFor={id} | |||
className={clsx( | |||
'items-center justify-start rounded overflow-hidden ring-secondary/50 gap-4 leading-none select-none cursor-pointer', | |||
'peer-focus:outline-0 peer-focus:ring-4 peer-focus:ring-secondary/50', | |||
'active:ring-tertiary/50 active:ring-4', | |||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:ring-0', | |||
'text-primary peer-disabled:text-primary peer-focus:text-secondary peer-checked:text-tertiary active:text-tertiary', | |||
{ | |||
'flex w-full': block, | |||
'inline-flex max-w-full align-middle': !block, | |||
}, | |||
{ | |||
'pl-2 pr-2': compact, | |||
'pl-4 pr-4': !compact, | |||
}, | |||
{ | |||
'border-2 border-primary peer-disabled:border-primary peer-focus:border-secondary peer-checked:border-tertiary active:border-tertiary': variant !== 'bare', | |||
'bg-primary text-negative peer-disabled:bg-primary peer-focus:bg-secondary peer-checked:bg-tertiary active:bg-tertiary': variant === 'filled', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
className, | |||
)} | |||
> | |||
<span | |||
className={clsx( | |||
'w-6 h-6 block rounded-full border-2 p-0.5 box-border', | |||
{ | |||
'-mr-2': compact, | |||
'border-current': variant !== 'filled', | |||
'border-negative': variant === 'filled', | |||
}, | |||
)} | |||
> | |||
<span | |||
className={clsx( | |||
'w-full h-full rounded-full bg-current', | |||
{ | |||
'text-current': variant !== 'filled', | |||
'text-negative': variant === 'filled', | |||
}, | |||
)} | |||
/> | |||
</span> | |||
<span | |||
className={clsx( | |||
'contents', | |||
{ | |||
'text-current': variant !== 'filled', | |||
'text-negative': variant === 'filled', | |||
}, | |||
)} | |||
> | |||
<span | |||
className={clsx( | |||
'flex-auto min-w-0', | |||
)} | |||
> | |||
<span | |||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||
data-testid="children" | |||
> | |||
{children} | |||
</span> | |||
{subtext && ( | |||
<> | |||
<span className="sr-only"> | |||
{' - '} | |||
</span> | |||
<span | |||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||
data-testid="subtext" | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
)} | |||
</span> | |||
{badge && ( | |||
<> | |||
<span className="sr-only"> | |||
{' - '} | |||
</span> | |||
<span | |||
data-testid="badge" | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
)} | |||
</span> | |||
</label> | |||
</> | |||
); | |||
}); | |||
RadioButton.displayName = 'RadioButton'; | |||
RadioButton.defaultProps = { | |||
badge: undefined, | |||
block: false, | |||
compact: false, | |||
subtext: undefined, | |||
size: 'medium', | |||
variant: 'filled', | |||
}; |
@@ -0,0 +1,7 @@ | |||
.tesseract-design-radio-tick-box + label + label > :first-child > :first-child { | |||
display: none; | |||
} | |||
.tesseract-design-radio-tick-box:checked + label + label > :first-child > :first-child { | |||
display: block; | |||
} |
@@ -0,0 +1,92 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
export type RadioTickBoxDerivedElement = HTMLInputElement; | |||
export interface RadioTickBoxProps extends Omit<React.InputHTMLAttributes<RadioTickBoxDerivedElement>, 'type' | 'size'> { | |||
block?: boolean; | |||
subtext?: React.ReactNode; | |||
} | |||
export const RadioTickBox = React.forwardRef<RadioTickBoxDerivedElement, RadioTickBoxProps>(( | |||
{ | |||
children, | |||
block = false, | |||
id: idProp, | |||
className, | |||
subtext, | |||
style, | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => { | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'gap-x-4 flex-wrap', | |||
block && 'flex', | |||
!block && 'inline-flex align-center', | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
type="radio" | |||
id={id} | |||
className="sr-only peer/radio tesseract-design-radio-tick-box" | |||
/> | |||
<label | |||
htmlFor={id} | |||
className="peer/children order-2 cursor-pointer peer-disabled/radio:cursor-not-allowed" | |||
> | |||
<span | |||
data-testid="children" | |||
> | |||
{children} | |||
</span> | |||
</label> | |||
<label | |||
htmlFor={id} | |||
className={clsx( | |||
'order-1 block rounded-full ring-secondary/50 overflow-hidden gap-4 leading-none select-none cursor-pointer', | |||
'peer-focus/radio:outline-0 peer-focus/radio:ring-4 peer-focus/radio:ring-secondary/50', | |||
'active:ring-tertiary/50 active:ring-4', | |||
'peer-active/children:ring-tertiary/50 peer-active/children:ring-4 peer-active/children:text-tertiary', | |||
'peer-disabled/radio:opacity-50 peer-disabled/radio:cursor-not-allowed peer-disabled/radio:ring-0', | |||
'text-primary peer-disabled/radio:text-primary peer-focus/radio:text-secondary peer-checked/radio:text-tertiary active:text-tertiary', | |||
)} | |||
> | |||
<span | |||
className={clsx( | |||
'w-6 h-6 block rounded-full border-2 p-0.5 box-border border-current', | |||
)} | |||
> | |||
<span | |||
className={clsx( | |||
'w-full h-full rounded-full bg-current text-current', | |||
)} | |||
/> | |||
</span> | |||
</label> | |||
{subtext && ( | |||
<div | |||
className="block w-full font-semi-expanded text-xs pl-10 order-3" | |||
data-testid="subtext" | |||
> | |||
{subtext} | |||
</div> | |||
)} | |||
</div> | |||
); | |||
}); | |||
RadioTickBox.displayName = 'RadioTickBox'; | |||
RadioTickBox.defaultProps = { | |||
block: false, | |||
subtext: undefined, | |||
}; |
@@ -3,7 +3,3 @@ export * from './components/DropdownSelect'; | |||
export * from './components/MenuSelect'; | |||
export * from './components/RadioButton'; | |||
export * from './components/RadioTickBox'; | |||
export * from './components/TagInput'; | |||
export * from './components/ToggleButton'; | |||
export * from './components/ToggleSwitch'; | |||
export * from './components/ToggleTickBox'; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
/// <reference types="vitest" /> | |||
export default ({ | |||
test: { | |||
global: true, | |||
environment: 'jsdom', | |||
}, | |||
}); |
@@ -0,0 +1,12 @@ | |||
{ | |||
"root": true, | |||
"rules": { | |||
"react/jsx-props-no-spreading": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -0,0 +1,80 @@ | |||
{ | |||
"name": "@tesseract-design/web-color-react", | |||
"version": "0.0.0", | |||
"files": [ | |||
"dist", | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
"react-dom": "^16.8 || ^17.0 || ^18.0" | |||
}, | |||
"scripts": { | |||
"prepublishOnly": "pridepack clean && pridepack build", | |||
"build": "pridepack build", | |||
"type-check": "pridepack check", | |||
"lint": "pridepack lint", | |||
"clean": "pridepack clean", | |||
"watch": "pridepack watch", | |||
"start": "pridepack start", | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": false, | |||
"description": "Tesseract components for inputting color data.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
}, | |||
"homepage": "", | |||
"bugs": { | |||
"url": "" | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"clsx": "^1.2.1" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
"require": "./dist/cjs/development/index.js", | |||
"import": "./dist/esm/development/index.js" | |||
}, | |||
"require": "./dist/cjs/production/index.js", | |||
"import": "./dist/esm/production/index.js", | |||
"types": "./dist/types/index.d.ts" | |||
} | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,102 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
type RgbTuple = [number, number, number]; | |||
export type SwatchDerivedElement = HTMLInputElement; | |||
export interface SwatchProps extends Omit<React.HTMLProps<SwatchDerivedElement>, 'color'> { | |||
color: RgbTuple; | |||
mode?: 'rgb' | 'hsl'; | |||
} | |||
export const useSwatchControls = () => { | |||
const id = React.useId(); | |||
const copyColor: React.ReactEventHandler<SwatchDerivedElement> = React.useCallback((e) => { | |||
const { value } = e.currentTarget; | |||
// eslint-disable-next-line no-void | |||
void window.navigator.clipboard.writeText(value); | |||
}, []); | |||
return React.useMemo(() => ({ | |||
id, | |||
copyColor, | |||
}), [id, copyColor]); | |||
}; | |||
export const Swatch = React.forwardRef<SwatchDerivedElement, SwatchProps>(({ | |||
color, | |||
mode = 'rgb', | |||
className, | |||
style, | |||
...etcProps | |||
}, forwardedRef) => { | |||
const { id, copyColor } = useSwatchControls(); | |||
const colorValue = `${mode}(${color.join(', ')})`; | |||
return ( | |||
<span | |||
className={clsx( | |||
'inline-block align-middle', | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
type="text" | |||
value={colorValue} | |||
className="sr-only select-all peer" | |||
readOnly | |||
id={id} | |||
onSelect={copyColor} | |||
/> | |||
<label | |||
className={clsx( | |||
'relative rounded ring-secondary/50 whitespace-nowrap inline-block align-top leading-none cursor-pointer', // todo eyedropper cursor | |||
'peer-focus:outline-0 peer-focus:ring-4', | |||
'peer-active:ring-tertiary/50', | |||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed', | |||
)} | |||
title={colorValue} | |||
htmlFor={id} | |||
> | |||
<span | |||
className="inline-block w-5 h-5 align-middle border border-[#ffffff]" | |||
> | |||
<span | |||
className="block w-full h-full border border-[#000000]" | |||
style={{ | |||
backgroundColor: `${mode}(${color.join(' ')})`, | |||
}} | |||
/> | |||
</span> | |||
<span className="tabular-nums text-xs sr-only"> | |||
{ | |||
color.map((c) => c | |||
.toString() | |||
.padStart(4, ' ') | |||
.split('') | |||
.map((cc, j) => ( | |||
<span | |||
key={`${cc}:${j}`} | |||
className={clsx({ | |||
'opacity-0': cc === ' ', | |||
})} | |||
> | |||
{j === 0 && ' '} | |||
{cc === ' ' && j > 0 ? '0' : cc} | |||
</span> | |||
))) | |||
} | |||
</span> | |||
</label> | |||
</span> | |||
); | |||
}); | |||
Swatch.displayName = 'Swatch'; | |||
Swatch.defaultProps = { | |||
mode: 'rgb', | |||
}; |
@@ -0,0 +1 @@ | |||
export * from './components/Swatch'; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
/// <reference types="vitest" /> | |||
export default ({ | |||
test: { | |||
global: true, | |||
environment: 'jsdom', | |||
}, | |||
}); |
@@ -0,0 +1,13 @@ | |||
{ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -6,32 +6,27 @@ | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=10" | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"dependencies": { | |||
"@tesseract-design/web-base-textcontrol": "link:../../../base/textcontrol" | |||
}, | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.4", | |||
"@testing-library/react": "^13.3.0", | |||
"@testing-library/react-hooks": "^8.0.1", | |||
"@testing-library/user-event": "^13.5.0", | |||
"@types/node": "^18.0.0", | |||
"@types/react": "^18.0.14", | |||
"eslint": "^8.20.0", | |||
"eslint-config-lxsmnsyc": "^0.4.7", | |||
"jsdom": "^20.0.0", | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.4.0", | |||
"typescript": "^4.7.4", | |||
"vitest": "^0.31.0" | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -48,8 +43,8 @@ | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": true, | |||
"description": "Formatted components for Tesseract for use in React.", | |||
"private": false, | |||
"description": "Tesseract components for inputting formatted data.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
@@ -60,9 +55,15 @@ | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "restricted" | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"clsx": "^1.2.1", | |||
"@tesseract-design/web-base": "workspace:*" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
@@ -76,7 +77,5 @@ | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
}, | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js" | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,214 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type EmailInputDerivedElement = HTMLInputElement; | |||
export interface EmailInputProps extends Omit<React.HTMLProps<EmailInputDerivedElement>, 'size' | 'type' | 'label' | 'inputMode'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* Additional description, usually graphical, indicating the nature of the component's value. | |||
*/ | |||
indicator?: React.ReactNode, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
*/ | |||
export const EmailInput = React.forwardRef<EmailInputDerivedElement, EmailInputProps>( | |||
( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
id: idProp, | |||
style, | |||
...etcProps | |||
}: EmailInputProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
aria-labelledby={labelId} | |||
type="email" | |||
data-testid="input" | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
id={labelId} | |||
htmlFor={id} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative select-none', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative select-none', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}, | |||
); | |||
EmailInput.displayName = 'EmailInput'; | |||
EmailInput.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
indicator: undefined, | |||
size: 'medium', | |||
border: false, | |||
block: false, | |||
variant: 'default', | |||
hiddenLabel: false, | |||
}; |
@@ -0,0 +1,215 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type PhoneNumberInputDerivedElement = HTMLInputElement; | |||
export interface PhoneNumberInputProps extends Omit<React.HTMLProps<PhoneNumberInputDerivedElement>, 'size' | 'type' | 'label' | 'inputMode'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* Additional description, usually graphical, indicating the nature of the component's value. | |||
*/ | |||
indicator?: React.ReactNode, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
*/ | |||
export const PhoneNumberInput = React.forwardRef< | |||
PhoneNumberInputDerivedElement, | |||
PhoneNumberInputProps | |||
>(( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
id: idProp, | |||
style, | |||
...etcProps | |||
}: PhoneNumberInputProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
aria-labelledby={labelId} | |||
type="tel" | |||
id={id} | |||
data-testid="input" | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
id={labelId} | |||
htmlFor={id} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative select-none', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative select-none', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}); | |||
PhoneNumberInput.displayName = 'PhoneNumberInput'; | |||
PhoneNumberInput.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
indicator: undefined, | |||
size: 'medium', | |||
border: false, | |||
block: false, | |||
variant: 'default', | |||
hiddenLabel: false, | |||
}; |
@@ -0,0 +1,212 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type UrlInputDerivedElement = HTMLInputElement; | |||
export interface UrlInputProps extends Omit<React.HTMLProps<UrlInputDerivedElement>, 'size' | 'type' | 'label' | 'inputMode'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* Additional description, usually graphical, indicating the nature of the component's value. | |||
*/ | |||
indicator?: React.ReactNode, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
*/ | |||
export const UrlInput = React.forwardRef<UrlInputDerivedElement, UrlInputProps>(( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
style, | |||
id: idProp, | |||
...etcProps | |||
}: UrlInputProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
aria-labelledby={labelId} | |||
type="url" | |||
data-testid="input" | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
htmlFor={id} | |||
id={labelId} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative select-none', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative select-none', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}); | |||
UrlInput.displayName = 'UrlInput'; | |||
UrlInput.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
indicator: undefined, | |||
size: 'medium', | |||
border: false, | |||
block: false, | |||
variant: 'default', | |||
hiddenLabel: false, | |||
}; |
@@ -1,3 +1,3 @@ | |||
export * from './components/EmailAddressInput'; | |||
export * from './components/EmailInput'; | |||
export * from './components/PhoneNumberInput'; | |||
export * from './components/UrlInput'; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
/// <reference types="vitest" /> | |||
export default ({ | |||
test: { | |||
global: true, | |||
environment: 'jsdom', | |||
}, | |||
}); |
@@ -0,0 +1,13 @@ | |||
{ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -6,32 +6,27 @@ | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=10" | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"dependencies": { | |||
"@tesseract-design/web-base-textcontrol": "link:../../../base/textcontrol" | |||
}, | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.4", | |||
"@testing-library/react": "^13.3.0", | |||
"@testing-library/react-hooks": "^8.0.1", | |||
"@testing-library/user-event": "^13.5.0", | |||
"@types/node": "^18.0.0", | |||
"@types/react": "^18.0.14", | |||
"eslint": "^8.20.0", | |||
"eslint-config-lxsmnsyc": "^0.4.7", | |||
"jsdom": "^20.0.0", | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.4.0", | |||
"typescript": "^4.7.4", | |||
"vitest": "^0.31.0" | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -48,8 +43,8 @@ | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": true, | |||
"description": "Freeform components for Tesseract for use in React.", | |||
"private": false, | |||
"description": "Tesseract components for inputting freeform data.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
@@ -60,9 +55,15 @@ | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "restricted" | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"clsx": "^1.2.1", | |||
"@tesseract-design/web-base": "workspace:*" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
@@ -76,7 +77,5 @@ | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
}, | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js" | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,215 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type MaskedTextInputDerivedElement = HTMLInputElement; | |||
export interface MaskedTextInputProps extends Omit<React.HTMLProps<MaskedTextInputDerivedElement>, 'size' | 'type' | 'label'> { | |||
/** | |||
* Short textual description indicating the nature of the component's value. | |||
*/ | |||
label?: React.ReactNode, | |||
/** | |||
* Short textual description as guidelines for valid input values. | |||
*/ | |||
hint?: React.ReactNode, | |||
/** | |||
* Size of the component. | |||
*/ | |||
size?: TextControl.Size, | |||
/** | |||
* Additional description, usually graphical, indicating the nature of the component's value. | |||
*/ | |||
indicator?: React.ReactNode, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
*/ | |||
export const MaskedTextInput = React.forwardRef< | |||
MaskedTextInputDerivedElement, | |||
MaskedTextInputProps | |||
>(( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
id: idProp, | |||
style, | |||
...etcProps | |||
}: MaskedTextInputProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50 overflow-hidden', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
aria-labelledby={labelId} | |||
type="password" | |||
data-testid="input" | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
id={labelId} | |||
htmlFor={id} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative select-none', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative select-none', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}); | |||
MaskedTextInput.displayName = 'MaskedTextInput'; | |||
MaskedTextInput.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
size: 'medium', | |||
indicator: undefined, | |||
border: false, | |||
block: false, | |||
variant: 'default', | |||
hiddenLabel: false, | |||
}; |
@@ -0,0 +1,230 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type MultilineTextInputDerivedElement = HTMLTextAreaElement; | |||
export interface MultilineTextInputProps extends Omit<React.HTMLProps<MultilineTextInputDerivedElement>, 'size' | 'label' | 'inputMode'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* Additional description, usually graphical, indicating the nature of the component's value. | |||
*/ | |||
indicator?: React.ReactNode, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
* | |||
* This component supports multiline input and adjusts its layout accordingly. | |||
*/ | |||
export const MultilineTextInput = React.forwardRef< | |||
MultilineTextInputDerivedElement, | |||
MultilineTextInputProps | |||
>(( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
style, | |||
id: idProp, | |||
...etcProps | |||
}: MultilineTextInputProps, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50 overflow-hidden', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<textarea | |||
{...etcProps} | |||
ref={forwardedRef} | |||
aria-labelledby={labelId} | |||
id={id} | |||
data-testid="input" | |||
style={{ | |||
height: 0, | |||
}} | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'resize': !block, | |||
'resize-y': block, | |||
}, | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate' && size === 'small', | |||
'pt-5': variant === 'alternate' && size === 'medium', | |||
'pt-8': variant === 'alternate' && size === 'large', | |||
}, | |||
{ | |||
'py-2.5': variant === 'default' && size === 'small', | |||
'py-3': variant === 'default' && size === 'medium', | |||
'py-5': variant === 'default' && size === 'large', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'min-h-10': size === 'small', | |||
'min-h-12': size === 'medium', | |||
'min-h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
id={labelId} | |||
htmlFor={id} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative select-none', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative select-none', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}); | |||
MultilineTextInput.displayName = 'MultilineTextInput'; | |||
MultilineTextInput.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
size: 'medium', | |||
indicator: undefined, | |||
border: false, | |||
block: false, | |||
variant: 'default', | |||
hiddenLabel: false, | |||
}; |
@@ -0,0 +1,234 @@ | |||
import * as React from 'react'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
export type TextInputDerivedElement = HTMLInputElement; | |||
export interface TextInputProps extends Omit<React.HTMLProps<TextInputDerivedElement>, 'size' | 'type' | 'label' | 'list' | 'inputMode'> { | |||
/** | |||
* 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?: TextControl.Size, | |||
/** | |||
* 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?: TextControl.InputType, | |||
/** | |||
* Style of the component. | |||
*/ | |||
variant?: TextControl.Variant, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
/** | |||
* Input mode of the component. | |||
*/ | |||
inputMode?: TextControl.InputMode, | |||
} | |||
/** | |||
* Component for inputting textual values. | |||
* | |||
* This component supports multiline input and adjusts its layout accordingly. | |||
*/ | |||
export const TextInput = React.forwardRef<TextInputDerivedElement, TextInputProps>(( | |||
{ | |||
label, | |||
hint, | |||
indicator, | |||
size = 'medium' as const, | |||
border = false, | |||
block = false, | |||
type = 'text' as const, | |||
variant = 'default' as const, | |||
hiddenLabel = false, | |||
className, | |||
id: idProp, | |||
style, | |||
inputMode = type, | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => { | |||
const labelId = React.useId(); | |||
const defaultId = React.useId(); | |||
const id = idProp ?? defaultId; | |||
let resultInputMode = inputMode as React.HTMLProps<TextInputDerivedElement>['inputMode']; | |||
if (type === 'text' && resultInputMode === 'search') { | |||
resultInputMode = 'text'; | |||
} else if (type === 'search' && resultInputMode === 'text') { | |||
resultInputMode = 'search'; | |||
} | |||
return ( | |||
<div | |||
className={clsx( | |||
'relative rounded ring-secondary/50 overflow-hidden', | |||
'focus-within:ring-4', | |||
{ | |||
'block': block, | |||
'inline-block align-middle': !block, | |||
}, | |||
className, | |||
)} | |||
style={style} | |||
> | |||
<input | |||
{...etcProps} | |||
ref={forwardedRef} | |||
aria-labelledby={labelId} | |||
id={id} | |||
type={type} | |||
inputMode={resultInputMode} | |||
data-testid="input" | |||
className={clsx( | |||
'bg-negative rounded-inherit w-full peer block', | |||
'focus:outline-0', | |||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||
{ | |||
'text-xxs': size === 'small', | |||
'text-xs': size === 'medium', | |||
}, | |||
{ | |||
'pl-4': variant === 'default', | |||
'pl-1.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-4': variant === 'alternate', | |||
}, | |||
{ | |||
'pr-4': variant === 'default' && !indicator, | |||
'pr-1.5': variant === 'alternate' && !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
)} | |||
/> | |||
{ | |||
label && ( | |||
<label | |||
data-testid="label" | |||
htmlFor={id} | |||
id={labelId} | |||
className={clsx( | |||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative select-none', | |||
{ | |||
'sr-only': hiddenLabel, | |||
}, | |||
{ | |||
'pr-1': !indicator, | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||
{label} | |||
</span> | |||
</label> | |||
) | |||
} | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
className={clsx( | |||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative select-none', | |||
{ | |||
'bottom-0 pl-4 pb-1': variant === 'default', | |||
'top-0.5': variant === 'alternate', | |||
}, | |||
{ | |||
'pt-2': variant === 'alternate' && size === 'small', | |||
'pt-3': variant === 'alternate' && size !== 'small', | |||
}, | |||
{ | |||
'pr-4': !indicator && variant === 'default', | |||
'pr-1': !indicator && variant === 'alternate', | |||
}, | |||
{ | |||
'pr-10': indicator && size === 'small', | |||
'pr-12': indicator && size === 'medium', | |||
'pr-16': indicator && size === 'large', | |||
}, | |||
)} | |||
> | |||
<div | |||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||
> | |||
{hint} | |||
</div> | |||
</div> | |||
)} | |||
{indicator && ( | |||
<div | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
'w-10': size === 'small', | |||
'w-12': size === 'medium', | |||
'w-16': size === 'large', | |||
}, | |||
)} | |||
> | |||
{indicator} | |||
</div> | |||
)} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||
/> | |||
) | |||
} | |||
</div> | |||
); | |||
}); | |||
TextInput.displayName = 'TextInput'; | |||
TextInput.defaultProps = { | |||
label: undefined, | |||
hint: undefined, | |||
size: 'medium', | |||
indicator: undefined, | |||
border: false, | |||
block: false, | |||
type: 'text', | |||
variant: 'default', | |||
hiddenLabel: false, | |||
inputMode: 'text', | |||
}; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
/// <reference types="vitest" /> | |||
export default ({ | |||
test: { | |||
global: true, | |||
environment: 'jsdom', | |||
}, | |||
}); |
@@ -0,0 +1,12 @@ | |||
{ | |||
"root": true, | |||
"rules": { | |||
"react/jsx-props-no-spreading": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -6,31 +6,27 @@ | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=10" | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"dependencies": { | |||
"@tesseract-design/web-base-badge": "link:../../../base/badge" | |||
}, | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.4", | |||
"@testing-library/react": "^13.3.0", | |||
"@testing-library/react-hooks": "^8.0.1", | |||
"@types/node": "^18.0.0", | |||
"@types/react": "^18.0.14", | |||
"eslint": "^8.20.0", | |||
"eslint-config-lxsmnsyc": "^0.4.7", | |||
"jsdom": "^20.0.0", | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.4.0", | |||
"typescript": "^4.7.4", | |||
"vitest": "^0.31.0" | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -47,8 +43,8 @@ | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": true, | |||
"description": "Information components for Tesseract for use in React.", | |||
"private": false, | |||
"description": "Tesseract components for displaying data.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
@@ -59,9 +55,14 @@ | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "restricted" | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"clsx": "^1.2.1" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
@@ -75,7 +76,5 @@ | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
}, | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js" | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"target": "es2018" | |||
} |
@@ -0,0 +1,42 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
export type BadgeDerivedElement = HTMLSpanElement; | |||
export interface BadgeProps extends React.HTMLProps<BadgeDerivedElement> { | |||
rounded?: boolean; | |||
} | |||
export const Badge = React.forwardRef<BadgeDerivedElement, BadgeProps>(( | |||
{ | |||
children, | |||
rounded = false, | |||
className, | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => ( | |||
<span | |||
{...etcProps} | |||
ref={forwardedRef} | |||
className={clsx( | |||
'relative h-6 min-w-6 flex items-center justify-center text-xs font-bold overflow-hidden font-semi-expanded', | |||
'before:absolute before:top-0 before:left-0 before:w-full before:h-full before:bg-current before:opacity-25', | |||
{ | |||
'rounded-full px-2': rounded, | |||
'rounded px-1': !rounded, | |||
}, | |||
className, | |||
)} | |||
> | |||
<span className="relative w-full"> | |||
{children} | |||
</span> | |||
</span> | |||
)); | |||
Badge.displayName = 'Badge'; | |||
Badge.defaultProps = { | |||
rounded: false, | |||
}; |
@@ -0,0 +1,65 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
export type KeyValueTableDerivedElement = HTMLDListElement; | |||
interface KeyValueProperty { | |||
key: string; | |||
className?: string; | |||
valueProps?: React.HTMLProps<HTMLElement>; | |||
} | |||
export interface KeyValueTableProps extends Omit<React.HTMLProps<KeyValueTableDerivedElement>, 'children'> { | |||
hiddenKeys?: boolean; | |||
properties?: (KeyValueProperty | boolean)[]; | |||
} | |||
export const KeyValueTable = React.forwardRef<KeyValueTableDerivedElement, KeyValueTableProps>(( | |||
{ | |||
hiddenKeys = false, | |||
properties = [], | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => ( | |||
<dl | |||
{...etcProps} | |||
className={clsx( | |||
'grid gap-y-1 grid-cols-3', | |||
)} | |||
ref={forwardedRef} | |||
> | |||
{ | |||
properties.map((property) => typeof property === 'object' && ( | |||
<div | |||
key={property.key} | |||
className={clsx('contents', property.className)} | |||
> | |||
<dt | |||
className={clsx(hiddenKeys && 'sr-only', 'pr-4')} | |||
> | |||
{property.key} | |||
</dt> | |||
<dd | |||
{...(property.valueProps ?? {})} | |||
className={clsx( | |||
'm-0 text-ellipsis overflow-hidden', | |||
!hiddenKeys && 'col-span-2', | |||
hiddenKeys && 'col-span-3', | |||
property.valueProps?.className, | |||
)} | |||
> | |||
{property.valueProps?.children} | |||
</dd> | |||
</div> | |||
)) | |||
} | |||
</dl> | |||
)); | |||
KeyValueTable.displayName = 'KeyValueTable'; | |||
KeyValueTable.defaultProps = { | |||
hiddenKeys: false, | |||
properties: [], | |||
}; |
@@ -0,0 +1,2 @@ | |||
export * from './components/Badge'; | |||
export * from './components/KeyValueTable'; |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types", "test"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
{ | |||
"exclude": ["node_modules"], | |||
"include": ["src", "types"], | |||
"compilerOptions": { | |||
"module": "ESNext", | |||
"lib": ["DOM", "ESNext"], | |||
"importHelpers": true, | |||
"declaration": true, | |||
"sourceMap": true, | |||
"rootDir": "./src", | |||
"strict": true, | |||
"noUnusedLocals": true, | |||
"noUnusedParameters": true, | |||
"noImplicitReturns": true, | |||
"noFallthroughCasesInSwitch": true, | |||
"moduleResolution": "node", | |||
"jsx": "react", | |||
"esModuleInterop": true, | |||
"target": "es2018" | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
/// <reference types="vitest" /> | |||
export default ({ | |||
test: { | |||
global: true, | |||
environment: 'jsdom', | |||
}, | |||
}); |
@@ -0,0 +1,13 @@ | |||
{ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
], | |||
"parserOptions": { | |||
"project": "./tsconfig.eslint.json" | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
lerna-debug.log* | |||
# Diagnostic reports (https://nodejs.org/api/report.html) | |||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | |||
# Runtime data | |||
pids | |||
*.pid | |||
*.seed | |||
*.pid.lock | |||
# Directory for instrumented libs generated by jscoverage/JSCover | |||
lib-cov | |||
# Coverage directory used by tools like istanbul | |||
coverage | |||
*.lcov | |||
# nyc test coverage | |||
.nyc_output | |||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | |||
.grunt | |||
# Bower dependency directory (https://bower.io/) | |||
bower_components | |||
# node-waf configuration | |||
.lock-wscript | |||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||
build/Release | |||
# Dependency directories | |||
node_modules/ | |||
jspm_packages/ | |||
# TypeScript v1 declaration files | |||
typings/ | |||
# TypeScript cache | |||
*.tsbuildinfo | |||
# Optional npm cache directory | |||
.npm | |||
# Optional eslint cache | |||
.eslintcache | |||
# Microbundle cache | |||
.rpt2_cache/ | |||
.rts2_cache_cjs/ | |||
.rts2_cache_es/ | |||
.rts2_cache_umd/ | |||
# Optional REPL history | |||
.node_repl_history | |||
# Output of 'npm pack' | |||
*.tgz | |||
# Yarn Integrity file | |||
.yarn-integrity | |||
# dotenv environment variables file | |||
.env | |||
.env.production | |||
.env.development | |||
# parcel-bundler cache (https://parceljs.org/) | |||
.cache | |||
# Next.js build output | |||
.next | |||
# Nuxt.js build / generate output | |||
.nuxt | |||
dist | |||
# Gatsby files | |||
.cache/ | |||
# Comment in the public line in if your project uses Gatsby and *not* Next.js | |||
# https://nextjs.org/blog/next-9-1#public-directory-support | |||
# public | |||
# vuepress build output | |||
.vuepress/dist | |||
# Serverless directories | |||
.serverless/ | |||
# FuseBox cache | |||
.fusebox/ | |||
# DynamoDB Local files | |||
.dynamodb/ | |||
# TernJS port file | |||
.tern-port | |||
.npmrc |
@@ -0,0 +1,7 @@ | |||
MIT License Copyright (c) 2023 TheoryOfNekomata <allan.crisostomo@outlook.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -0,0 +1,88 @@ | |||
{ | |||
"name": "@tesseract-design/web-multichoice-react", | |||
"version": "0.0.0", | |||
"files": [ | |||
"dist", | |||
"src" | |||
], | |||
"engines": { | |||
"node": ">=12" | |||
}, | |||
"license": "MIT", | |||
"keywords": [ | |||
"pridepack" | |||
], | |||
"devDependencies": { | |||
"@testing-library/jest-dom": "^5.16.5", | |||
"@testing-library/react": "^13.4.0", | |||
"@types/node": "^18.14.1", | |||
"@types/react": "^18.0.27", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
"pridepack": "2.4.4", | |||
"react": "^18.2.0", | |||
"react-dom": "^18.2.0", | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.5.0", | |||
"tsx": "^3.12.7", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
"react-dom": "^16.8 || ^17.0 || ^18.0" | |||
}, | |||
"scripts": { | |||
"prepublishOnly": "pridepack clean && pridepack build", | |||
"build": "pridepack build && tsx scripts/build.ts", | |||
"type-check": "pridepack check", | |||
"lint": "pridepack lint", | |||
"clean": "pridepack clean", | |||
"watch": "pridepack watch", | |||
"start": "pridepack start", | |||
"dev": "pridepack dev", | |||
"test": "vitest" | |||
}, | |||
"private": false, | |||
"description": "Tesseract components for selecting multiple values across an array of options.", | |||
"repository": { | |||
"url": "", | |||
"type": "git" | |||
}, | |||
"homepage": "", | |||
"bugs": { | |||
"url": "" | |||
}, | |||
"author": "TheoryOfNekomata <allan.crisostomo@outlook.com>", | |||
"publishConfig": { | |||
"access": "public" | |||
}, | |||
"dependencies": { | |||
"@modal-sh/react-utils": "workspace:*", | |||
"@tesseract-design/web-base": "workspace:*", | |||
"clsx": "^1.2.1", | |||
"react-tag-input-component": "^2.0.2" | |||
}, | |||
"types": "./dist/types/index.d.ts", | |||
"main": "./dist/cjs/production/index.js", | |||
"module": "./dist/esm/production/index.js", | |||
"exports": { | |||
".": { | |||
"development": { | |||
"require": "./dist/cjs/development/index.js", | |||
"import": "./dist/esm/development/index.js" | |||
}, | |||
"require": "./dist/cjs/production/index.js", | |||
"import": "./dist/esm/production/index.js", | |||
"types": "./dist/types/index.d.ts" | |||
}, | |||
"./dist/TagInput.css": "./dist/TagInput.css", | |||
"./dist/ToggleButton.css": "./dist/ToggleButton.css", | |||
"./dist/ToggleSwitch.css": "./dist/ToggleSwitch.css", | |||
"./dist/ToggleTickBox.css": "./dist/ToggleTickBox.css" | |||
}, | |||
"typesVersions": { | |||
"*": {} | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
{ | |||
"target": "es2018", | |||
"entryPoints": { | |||
".": "src/index.ts", | |||
"./dist/TagInput.css": "./dist/TagInput/TagInput.css", | |||
"./dist/ToggleButton.css": "./dist/ToggleButton/ToggleButton.css", | |||
"./dist/ToggleSwitch.css": "./dist/ToggleSwitch/ToggleSwitch.css", | |||
"./dist/ToggleTickBox.css": "./dist/ToggleTickBox/ToggleTickBox.css" | |||
} | |||
} |