Browse Source

Merge pull request 'refactor/tailwind' (#3) from refactor/tailwind into master

Reviewed-on: https://code.modal.sh/tesseract-design/tesseract/pulls/3
master
TheoryOfNekomata 1 year ago
parent
commit
6ec9707a3a
100 changed files with 5024 additions and 108 deletions
  1. +60
    -0
      TODO.md
  2. +9
    -0
      base/.eslintrc
  3. +107
    -0
      base/.gitignore
  4. +7
    -0
      base/LICENSE
  5. +14
    -15
      base/package.json
  6. +3
    -0
      base/pridepack.json
  7. +5
    -0
      base/src/button.ts
  8. +2
    -0
      base/src/index.ts
  9. +7
    -0
      base/src/text-control.ts
  10. +21
    -0
      base/tsconfig.eslint.json
  11. +21
    -0
      base/tsconfig.json
  12. +14
    -0
      categories/action/react/.eslintrc
  13. +107
    -0
      categories/action/react/.gitignore
  14. +7
    -0
      categories/action/react/LICENSE
  15. +21
    -22
      categories/action/react/package.json
  16. +3
    -0
      categories/action/react/pridepack.json
  17. +134
    -0
      categories/action/react/src/components/ActionButton/index.tsx
  18. +0
    -0
      categories/action/react/src/index.ts
  19. +21
    -0
      categories/action/react/tsconfig.eslint.json
  20. +21
    -0
      categories/action/react/tsconfig.json
  21. +8
    -0
      categories/action/react/vitest.config.ts
  22. +22
    -0
      categories/action/swift-ui/Package.swift
  23. +4
    -0
      categories/action/swift-ui/src/components/ActionButton/ActionButton.swift
  24. +9
    -0
      categories/blob/react/.eslintrc
  25. +107
    -0
      categories/blob/react/.gitignore
  26. +7
    -0
      categories/blob/react/LICENSE
  27. +81
    -0
      categories/blob/react/package.json
  28. +3
    -0
      categories/blob/react/pridepack.json
  29. +300
    -0
      categories/blob/react/src/components/FileSelectBox/index.tsx
  30. +1
    -0
      categories/blob/react/src/index.ts
  31. +21
    -0
      categories/blob/react/tsconfig.eslint.json
  32. +21
    -0
      categories/blob/react/tsconfig.json
  33. +8
    -0
      categories/blob/react/vitest.config.ts
  34. +13
    -0
      categories/choice/react/.eslintrc
  35. +107
    -0
      categories/choice/react/.gitignore
  36. +7
    -0
      categories/choice/react/LICENSE
  37. +84
    -0
      categories/choice/react/package.json
  38. +3
    -0
      categories/choice/react/pridepack.json
  39. +14
    -0
      categories/choice/react/scripts/build.ts
  40. +242
    -0
      categories/choice/react/src/components/ComboBox/index.tsx
  41. +216
    -0
      categories/choice/react/src/components/DropdownSelect/index.tsx
  42. +234
    -0
      categories/choice/react/src/components/MenuSelect/index.tsx
  43. +7
    -0
      categories/choice/react/src/components/RadioButton/RadioButton.css
  44. +153
    -0
      categories/choice/react/src/components/RadioButton/index.tsx
  45. +7
    -0
      categories/choice/react/src/components/RadioTickBox/RadioTickBox.css
  46. +92
    -0
      categories/choice/react/src/components/RadioTickBox/index.tsx
  47. +0
    -4
      categories/choice/react/src/index.ts
  48. +21
    -0
      categories/choice/react/tsconfig.eslint.json
  49. +21
    -0
      categories/choice/react/tsconfig.json
  50. +8
    -0
      categories/choice/react/vitest.config.ts
  51. +12
    -0
      categories/color/react/.eslintrc
  52. +107
    -0
      categories/color/react/.gitignore
  53. +7
    -0
      categories/color/react/LICENSE
  54. +80
    -0
      categories/color/react/package.json
  55. +3
    -0
      categories/color/react/pridepack.json
  56. +102
    -0
      categories/color/react/src/components/Swatch/index.tsx
  57. +1
    -0
      categories/color/react/src/index.ts
  58. +21
    -0
      categories/color/react/tsconfig.eslint.json
  59. +21
    -0
      categories/color/react/tsconfig.json
  60. +8
    -0
      categories/color/react/vitest.config.ts
  61. +13
    -0
      categories/formatted/react/.eslintrc
  62. +107
    -0
      categories/formatted/react/.gitignore
  63. +7
    -0
      categories/formatted/react/LICENSE
  64. +21
    -22
      categories/formatted/react/package.json
  65. +3
    -0
      categories/formatted/react/pridepack.json
  66. +214
    -0
      categories/formatted/react/src/components/EmailInput/index.tsx
  67. +215
    -0
      categories/formatted/react/src/components/PhoneNumberInput/index.tsx
  68. +212
    -0
      categories/formatted/react/src/components/UrlInput/index.tsx
  69. +1
    -1
      categories/formatted/react/src/index.ts
  70. +21
    -0
      categories/formatted/react/tsconfig.eslint.json
  71. +21
    -0
      categories/formatted/react/tsconfig.json
  72. +8
    -0
      categories/formatted/react/vitest.config.ts
  73. +13
    -0
      categories/freeform/react/.eslintrc
  74. +107
    -0
      categories/freeform/react/.gitignore
  75. +7
    -0
      categories/freeform/react/LICENSE
  76. +21
    -22
      categories/freeform/react/package.json
  77. +3
    -0
      categories/freeform/react/pridepack.json
  78. +215
    -0
      categories/freeform/react/src/components/MaskedTextInput/index.tsx
  79. +230
    -0
      categories/freeform/react/src/components/MultilineTextInput/index.tsx
  80. +234
    -0
      categories/freeform/react/src/components/TextInput/index.tsx
  81. +1
    -1
      categories/freeform/react/src/index.ts
  82. +21
    -0
      categories/freeform/react/tsconfig.eslint.json
  83. +21
    -0
      categories/freeform/react/tsconfig.json
  84. +8
    -0
      categories/freeform/react/vitest.config.ts
  85. +12
    -0
      categories/information/react/.eslintrc
  86. +107
    -0
      categories/information/react/.gitignore
  87. +7
    -0
      categories/information/react/LICENSE
  88. +20
    -21
      categories/information/react/package.json
  89. +3
    -0
      categories/information/react/pridepack.json
  90. +42
    -0
      categories/information/react/src/components/Badge/index.tsx
  91. +65
    -0
      categories/information/react/src/components/KeyValueTable/index.tsx
  92. +2
    -0
      categories/information/react/src/index.ts
  93. +21
    -0
      categories/information/react/tsconfig.eslint.json
  94. +21
    -0
      categories/information/react/tsconfig.json
  95. +8
    -0
      categories/information/react/vitest.config.ts
  96. +13
    -0
      categories/multichoice/react/.eslintrc
  97. +107
    -0
      categories/multichoice/react/.gitignore
  98. +7
    -0
      categories/multichoice/react/LICENSE
  99. +88
    -0
      categories/multichoice/react/package.json
  100. +10
    -0
      categories/multichoice/react/pridepack.json

+ 60
- 0
TODO.md View File

@@ -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.

+ 9
- 0
base/.eslintrc View File

@@ -0,0 +1,9 @@
{
"root": true,
"extends": [
"lxsmnsyc/typescript"
],
"parserOptions": {
"project": "./tsconfig.eslint.json"
}
}

+ 107
- 0
base/.gitignore View File

@@ -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

+ 7
- 0
base/LICENSE View File

@@ -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.

packages/web/base/selectcontrol/package.json → base/package.json View File

@@ -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"
}
}

+ 3
- 0
base/pridepack.json View File

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

+ 5
- 0
base/src/button.ts View File

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

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

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

+ 2
- 0
base/src/index.ts View File

@@ -0,0 +1,2 @@
export * as Button from './button';
export * as TextControl from './text-control';

+ 7
- 0
base/src/text-control.ts View File

@@ -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;

+ 21
- 0
base/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
base/tsconfig.json View File

@@ -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"
}
}

+ 14
- 0
categories/action/react/.eslintrc View File

@@ -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"
}
}

+ 107
- 0
categories/action/react/.gitignore View File

@@ -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

+ 7
- 0
categories/action/react/LICENSE View File

@@ -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.

packages/web/categories/action/react/package.json → categories/action/react/package.json View File

@@ -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"
}
}

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

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

+ 134
- 0
categories/action/react/src/components/ActionButton/index.tsx View File

@@ -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,
};

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


+ 21
- 0
categories/action/react/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
categories/action/react/tsconfig.json View File

@@ -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"
}
}

+ 8
- 0
categories/action/react/vitest.config.ts View File

@@ -0,0 +1,8 @@
/// <reference types="vitest" />

export default ({
test: {
global: true,
environment: 'jsdom',
},
});

+ 22
- 0
categories/action/swift-ui/Package.swift View File

@@ -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",
),
]
)

+ 4
- 0
categories/action/swift-ui/src/components/ActionButton/ActionButton.swift View File

@@ -0,0 +1,4 @@
import SwiftUI

public struct ActionButton: View {
}

+ 9
- 0
categories/blob/react/.eslintrc View File

@@ -0,0 +1,9 @@
{
"root": true,
"extends": [
"lxsmnsyc/typescript/react"
],
"parserOptions": {
"project": "./tsconfig.eslint.json"
}
}

+ 107
- 0
categories/blob/react/.gitignore View File

@@ -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

+ 7
- 0
categories/blob/react/LICENSE View File

@@ -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.

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

@@ -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": {
"*": {}
}
}

+ 3
- 0
categories/blob/react/pridepack.json View File

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

+ 300
- 0
categories/blob/react/src/components/FileSelectBox/index.tsx View File

@@ -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';

+ 1
- 0
categories/blob/react/src/index.ts View File

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

+ 21
- 0
categories/blob/react/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
categories/blob/react/tsconfig.json View File

@@ -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"
}
}

+ 8
- 0
categories/blob/react/vitest.config.ts View File

@@ -0,0 +1,8 @@
/// <reference types="vitest" />

export default ({
test: {
global: true,
environment: 'jsdom',
},
});

+ 13
- 0
categories/choice/react/.eslintrc View File

@@ -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"
}
}

+ 107
- 0
categories/choice/react/.gitignore View File

@@ -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

+ 7
- 0
categories/choice/react/LICENSE View File

@@ -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.

+ 84
- 0
categories/choice/react/package.json View File

@@ -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": {
"*": {}
}
}

+ 3
- 0
categories/choice/react/pridepack.json View File

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

+ 14
- 0
categories/choice/react/scripts/build.ts View File

@@ -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');

+ 242
- 0
categories/choice/react/src/components/ComboBox/index.tsx View File

@@ -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',
};

+ 216
- 0
categories/choice/react/src/components/DropdownSelect/index.tsx View File

@@ -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,
};

+ 234
- 0
categories/choice/react/src/components/MenuSelect/index.tsx View File

@@ -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',
};

+ 7
- 0
categories/choice/react/src/components/RadioButton/RadioButton.css View File

@@ -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;
}

+ 153
- 0
categories/choice/react/src/components/RadioButton/index.tsx View File

@@ -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',
};

+ 7
- 0
categories/choice/react/src/components/RadioTickBox/RadioTickBox.css View File

@@ -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;
}

+ 92
- 0
categories/choice/react/src/components/RadioTickBox/index.tsx View File

@@ -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,
};

packages/web/categories/option/react/src/index.ts → categories/choice/react/src/index.ts View File

@@ -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';

+ 21
- 0
categories/choice/react/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
categories/choice/react/tsconfig.json View File

@@ -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"
}
}

+ 8
- 0
categories/choice/react/vitest.config.ts View File

@@ -0,0 +1,8 @@
/// <reference types="vitest" />

export default ({
test: {
global: true,
environment: 'jsdom',
},
});

+ 12
- 0
categories/color/react/.eslintrc View File

@@ -0,0 +1,12 @@
{
"root": true,
"rules": {
"react/jsx-props-no-spreading": "off"
},
"extends": [
"lxsmnsyc/typescript/react"
],
"parserOptions": {
"project": "./tsconfig.eslint.json"
}
}

+ 107
- 0
categories/color/react/.gitignore View File

@@ -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

+ 7
- 0
categories/color/react/LICENSE View File

@@ -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.

+ 80
- 0
categories/color/react/package.json View File

@@ -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": {
"*": {}
}
}

+ 3
- 0
categories/color/react/pridepack.json View File

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

+ 102
- 0
categories/color/react/src/components/Swatch/index.tsx View File

@@ -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',
};

+ 1
- 0
categories/color/react/src/index.ts View File

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

+ 21
- 0
categories/color/react/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
categories/color/react/tsconfig.json View File

@@ -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"
}
}

+ 8
- 0
categories/color/react/vitest.config.ts View File

@@ -0,0 +1,8 @@
/// <reference types="vitest" />

export default ({
test: {
global: true,
environment: 'jsdom',
},
});

+ 13
- 0
categories/formatted/react/.eslintrc View File

@@ -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"
}
}

+ 107
- 0
categories/formatted/react/.gitignore View File

@@ -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

+ 7
- 0
categories/formatted/react/LICENSE View File

@@ -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.

packages/web/categories/formatted/react/package.json → categories/formatted/react/package.json View File

@@ -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"
}
}

+ 3
- 0
categories/formatted/react/pridepack.json View File

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

+ 214
- 0
categories/formatted/react/src/components/EmailInput/index.tsx View File

@@ -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,
};

+ 215
- 0
categories/formatted/react/src/components/PhoneNumberInput/index.tsx View File

@@ -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,
};

+ 212
- 0
categories/formatted/react/src/components/UrlInput/index.tsx View File

@@ -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,
};

packages/web/categories/formatted/react/src/index.ts → categories/formatted/react/src/index.ts View File

@@ -1,3 +1,3 @@
export * from './components/EmailAddressInput';
export * from './components/EmailInput';
export * from './components/PhoneNumberInput';
export * from './components/UrlInput';

+ 21
- 0
categories/formatted/react/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
categories/formatted/react/tsconfig.json View File

@@ -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"
}
}

+ 8
- 0
categories/formatted/react/vitest.config.ts View File

@@ -0,0 +1,8 @@
/// <reference types="vitest" />

export default ({
test: {
global: true,
environment: 'jsdom',
},
});

+ 13
- 0
categories/freeform/react/.eslintrc View File

@@ -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"
}
}

+ 107
- 0
categories/freeform/react/.gitignore View File

@@ -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

+ 7
- 0
categories/freeform/react/LICENSE View File

@@ -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.

packages/web/categories/freeform/react/package.json → categories/freeform/react/package.json View File

@@ -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"
}
}

+ 3
- 0
categories/freeform/react/pridepack.json View File

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

+ 215
- 0
categories/freeform/react/src/components/MaskedTextInput/index.tsx View File

@@ -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,
};

+ 230
- 0
categories/freeform/react/src/components/MultilineTextInput/index.tsx View File

@@ -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,
};

+ 234
- 0
categories/freeform/react/src/components/TextInput/index.tsx View File

@@ -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',
};

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


+ 21
- 0
categories/freeform/react/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
categories/freeform/react/tsconfig.json View File

@@ -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"
}
}

+ 8
- 0
categories/freeform/react/vitest.config.ts View File

@@ -0,0 +1,8 @@
/// <reference types="vitest" />

export default ({
test: {
global: true,
environment: 'jsdom',
},
});

+ 12
- 0
categories/information/react/.eslintrc View File

@@ -0,0 +1,12 @@
{
"root": true,
"rules": {
"react/jsx-props-no-spreading": "off"
},
"extends": [
"lxsmnsyc/typescript/react"
],
"parserOptions": {
"project": "./tsconfig.eslint.json"
}
}

+ 107
- 0
categories/information/react/.gitignore View File

@@ -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

+ 7
- 0
categories/information/react/LICENSE View File

@@ -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.

packages/web/categories/information/react/package.json → categories/information/react/package.json View File

@@ -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"
}
}

+ 3
- 0
categories/information/react/pridepack.json View File

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

+ 42
- 0
categories/information/react/src/components/Badge/index.tsx View File

@@ -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,
};

+ 65
- 0
categories/information/react/src/components/KeyValueTable/index.tsx View File

@@ -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: [],
};

+ 2
- 0
categories/information/react/src/index.ts View File

@@ -0,0 +1,2 @@
export * from './components/Badge';
export * from './components/KeyValueTable';

+ 21
- 0
categories/information/react/tsconfig.eslint.json View File

@@ -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"
}
}

+ 21
- 0
categories/information/react/tsconfig.json View File

@@ -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"
}
}

+ 8
- 0
categories/information/react/vitest.config.ts View File

@@ -0,0 +1,8 @@
/// <reference types="vitest" />

export default ({
test: {
global: true,
environment: 'jsdom',
},
});

+ 13
- 0
categories/multichoice/react/.eslintrc View File

@@ -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"
}
}

+ 107
- 0
categories/multichoice/react/.gitignore View File

@@ -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

+ 7
- 0
categories/multichoice/react/LICENSE View File

@@ -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.

+ 88
- 0
categories/multichoice/react/package.json View File

@@ -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": {
"*": {}
}
}

+ 10
- 0
categories/multichoice/react/pridepack.json View File

@@ -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"
}
}

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

Loading…
Cancel
Save