Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
5d65e61
tailwind-ui package
camdendotlol Jul 17, 2025
97b25fc
linting for TS files (#533)
camdendotlol Jul 30, 2025
69e4661
new navbar branch after messy rebase (#535)
camdendotlol Jul 30, 2025
1de0250
Tailwind `Button` component (#534)
camdendotlol Jul 31, 2025
23eb9a9
Tailwind `Card` component (#536)
camdendotlol Aug 4, 2025
2415450
badge component (#537)
camdendotlol Aug 4, 2025
a5e9504
checkbox (#538)
camdendotlol Aug 4, 2025
74f7e9c
Tailwind `Dropdown` component (#539)
camdendotlol Aug 4, 2025
3bbc246
Tailwind `Listbox` component (#540)
camdendotlol Aug 5, 2025
5eed533
switch (#542)
camdendotlol Aug 6, 2025
ad04970
Performant UI `RadioGroup` component (#541)
camdendotlol Aug 6, 2025
e3cbcba
Performant UI `Dialog` component (#543)
camdendotlol Aug 6, 2025
ffc6e57
Performant UI `TextArea` component (#544)
camdendotlol Aug 6, 2025
789b61f
rename package to Performant UI
camdendotlol Aug 7, 2025
9d9bee3
Performant UI `Pagination` and `PageStats` components (#545)
camdendotlol Aug 12, 2025
eccf734
add exports
camdendotlol Aug 12, 2025
1ed1c20
match package number
camdendotlol Aug 12, 2025
23ee427
Merge branch 'master' into performant-ui
camdendotlol Aug 12, 2025
ba872a4
package.json update
camdendotlol Aug 12, 2025
08d2e4e
removed flow dev dependency from performant-ui
camdendotlol Aug 13, 2025
142a7a7
Performant UI `MediaSelect` component (#549)
camdendotlol Aug 13, 2025
57ea1a5
bump version
camdendotlol Aug 13, 2025
b6371d6
upgrade dependencies
camdendotlol Aug 13, 2025
ffea087
Performant UI `Table` component (#550)
camdendotlol Aug 13, 2025
b3119cf
version bump
camdendotlol Aug 13, 2025
cd5831a
build fixes
camdendotlol Aug 13, 2025
19e69ff
style and type fixes
camdendotlol Aug 14, 2025
949802b
subcomponent type fixes
camdendotlol Aug 14, 2025
12225c8
outline style consistency between input and textarea
camdendotlol Aug 15, 2025
81f9b48
fix input helper text styling
camdendotlol Aug 15, 2025
d3268da
mediaselect fixes
camdendotlol Aug 15, 2025
fbde3f6
various fixes
camdendotlol Aug 15, 2025
046599a
input and table fixes
camdendotlol Aug 15, 2025
afcb60c
Table API improvements
camdendotlol Aug 15, 2025
850a2d6
avatar component
camdendotlol Aug 18, 2025
603a95e
table and avatar fixes
camdendotlol Aug 18, 2025
a7c51ea
fix missing label for mediaselect
camdendotlol Aug 18, 2025
fddd233
update styling on checkbox
camdendotlol Aug 18, 2025
8b4295f
add displayNames to dropdown subcomponents
camdendotlol Aug 18, 2025
6dd1a26
update performant-ui version number
camdendotlol Aug 19, 2025
cf8ee54
fix navbar displayNames
camdendotlol Aug 19, 2025
4184e78
add padding to navbar
camdendotlol Aug 19, 2025
87a791c
remove dark classes
camdendotlol Aug 21, 2025
0be693e
bump version number
camdendotlol Aug 21, 2025
64b6bb9
onClick for Dropdown
camdendotlol Aug 26, 2025
098198d
bump version
camdendotlol Aug 26, 2025
7a7e33d
bump version
camdendotlol Aug 26, 2025
2589fa1
listbox improvements
camdendotlol Aug 27, 2025
341964e
type fixes
camdendotlol Aug 27, 2025
b37e5ef
bump version number
camdendotlol Aug 27, 2025
fea30ee
mediaselect changes
camdendotlol Aug 28, 2025
8032133
add disabled prop to RadioGroup and MediaSelect
camdendotlol Oct 6, 2025
e74d499
bump version
camdendotlol Oct 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
2 changes: 0 additions & 2 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,5 @@
[lints]

[options]
esproposal.export_star_as=enable
module.file_ext=.less

[strict]
124 changes: 89 additions & 35 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,57 @@
import js from '@eslint/js';
import babelParser from '@babel/eslint-parser';
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';
import flowtype from 'eslint-plugin-flowtype';
import { importX } from 'eslint-plugin-import-x';
import jest from 'eslint-plugin-jest';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import react from 'eslint-plugin-react';
import globals from 'globals';
import stylistic from '@stylistic/eslint-plugin';
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';

const commonRules = {
'class-methods-use-this': 'off',
'@stylistic/comma-dangle': ['error', 'never'],
"import-x/no-named-as-default": "off",
"import-x/no-named-as-default-member": "off",
'import-x/prefer-default-export': 'off',
'jsx-a11y/media-has-caption': 'off',
'@stylistic/jsx-quotes': ['error', 'prefer-single'],
'@stylistic/lines-between-class-members': 'off',
'@stylistic/max-len': ['error', {
code: 120,
ignoreStrings: true
}],
'no-underscore-dangle': 'off',
'no-use-before-define': 'off',
'@stylistic/quote-props': ['error', 'as-needed', {
keywords: false,
unnecessary: true,
numbers: true
}],
'react/display-name': 'off',
'react/default-props-match-prop-types': 'off',
'react/destructuring-assignment': 'off',
'react/jsx-no-bind': 'off',
'react/jsx-props-no-spreading': 'off',
'react/no-array-index-key': 'off',
'react/no-did-update-set-state': 'off',
'react/prefer-stateless-function': 'off',
'react/require-default-props': 'off',
'react/sort-comp': 'off',
'react/static-property-placement': 'off',
'@stylistic/semi-style': ['error', 'last'],
'@stylistic/semi': ['error', 'always']
};

export default [
importX.flatConfigs.recommended,
js.configs.recommended,
react.configs.flat.recommended,
{
files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
files: ['**/*.{js,jsx,mjs,cjs}'],
languageOptions: {
ecmaVersion: 'latest',
globals: {
Expand Down Expand Up @@ -48,48 +86,64 @@ export default [
'@stylistic': stylistic
},
rules: {
'class-methods-use-this': 'off',
'@stylistic/comma-dangle': ['error', 'never'],
"import-x/no-named-as-default": "off",
"import-x/no-named-as-default-member": "off",
'import-x/prefer-default-export': 'off',
'jsx-a11y/media-has-caption': 'off',
'@stylistic/jsx-quotes': ['error', 'prefer-single'],
'@stylistic/lines-between-class-members': 'off',
'@stylistic/max-len': ['error', {
code: 120,
ignoreStrings: true
}],
'no-underscore-dangle': 'off',
'no-use-before-define': 'off',
'@stylistic/quote-props': ['error', 'as-needed', {
keywords: false,
unnecessary: true,
numbers: true
}],
'react/display-name': 'off',
'react/default-props-match-prop-types': 'off',
'react/destructuring-assignment': 'off',
...commonRules,
'react/jsx-filename-extension': [1, {
extensions: ['.js', '.jsx']
}],
'react/jsx-no-bind': 'off',
'react/jsx-props-no-spreading': 'off',
'react/no-array-index-key': 'off',
'react/no-did-update-set-state': 'off',
'react/prefer-stateless-function': 'off',
'react/require-default-props': 'off',
'react/sort-comp': 'off',
'react/static-property-placement': 'off',
'@stylistic/semi-style': ['error', 'last'],
'@stylistic/semi': ['error', 'always']
}]
},
settings: {
react: {
version: 'detect'
}
}
}, {
},
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 'latest',
globals: {
...globals.browser,
...jest.environments.globals.globals,
__dirname: 'readonly',
Atomics: 'readonly',
JSX: 'readonly',
SharedArrayBuffer: 'readonly',
TimeoutID: 'readonly',
process: 'readonly'
},
parser: typescriptParser,
parserOptions: {
ecmaFeatures: {
jsx: true
},
project: './tsconfig.json'
},
sourceType: 'module'
},
plugins: {
react,
'@typescript-eslint': typescriptEslint,
jest,
'jsx-a11y': jsxA11y,
'@stylistic': stylistic
},
rules: {
...commonRules,
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error"]
},
settings: {
react: {
version: 'detect'
},
'import-x/resolver-next': [
createTypeScriptImportResolver({
project: 'packages/*/{ts,js}config.json'
})
]
}
},
{
ignores: [
'**/dist/*'
]
Expand Down
22 changes: 13 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,31 @@
"@babel/core": "^7.28.0",
"@babel/eslint-parser": "^7.28.0",
"@babel/preset-env": "^7.28.0",
"@eslint/compat": "^1.3.1",
"@babel/preset-typescript": "^7.27.1",
"@eslint/compat": "^1.3.2",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.31.0",
"@eslint/js": "^9.33.0",
"@stylistic/eslint-plugin": "^5.2.3",
"@typescript-eslint/eslint-plugin": "^8.39.1",
"@typescript-eslint/parser": "^8.39.1",
"babel-eslint": "^10.1.0",
"babel-jest": "^30.0.4",
"babel-jest": "^30.0.5",
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"eslint": "^9.31.0",
"eslint": "^9.33.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-storybook": "^9.0.17",
"flow-bin": "^0.275.0",
"eslint-plugin-storybook": "^9.1.2",
"flow-bin": "^0.278.0",
"globals": "^16.3.0",
"jest": "^27.5.1",
"minimist": "^1.2.6",
"@stylistic/eslint-plugin": "^5.2.2",
"underscore": "^1.13.4"
"minimist": "^1.2.8",
"underscore": "^1.13.7"
},
"jest": {
"projects": [
Expand Down
Binary file modified packages/.DS_Store
Binary file not shown.
32 changes: 32 additions & 0 deletions packages/performant-ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@performant-software/performant-ui",
"version": "0.0.1-beta.50",
"main": "./dist/index.cjs.js",
"module": "./dist/index.es.js",
"types": "./dist/performant-ui/src/index.d.ts",
"type": "module",
"scripts": {
"build": "vite build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"peerDependencies": {
"react": ">= 16.13.1 < 20.0.0",
"react-dom": ">= 16.13.1 < 20.0.0"
},
"dependencies": {
"clsx": "^2.1.1",
"@headlessui/react": "^2.2.6",
"react-icons": "^5.5.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.2.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"typescript": "^5.8.3",
"vite": "^5.1.4",
"vite-plugin-dts": "4.5.4"
}
}
48 changes: 48 additions & 0 deletions packages/performant-ui/src/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import clsx from 'clsx'
import React from 'react'

interface Props {
alt?: string
initials: string
src?: string
classes?: {
container?: string
img?: string
initials?: string
}
}

const Avatar: React.FC<Props> = (props) => {
return (
<div
className={clsx(
'overflow-hidden rounded-full bg-black text-white select-none w-10 h-10 font-sans',
props.classes?.container,
)}
>
{props.src
? (
<img
alt={props.alt}
className={clsx(
'object-cover w-full h-full',
props.classes?.img
)}
src={props.src}
/>
)
: (
<span
className={clsx(
'p-1 h-full w-full flex items-center justify-center',
props.classes?.initials
)}
>
{props.initials}
</span>
)}
</div>
)
}

export default Avatar
32 changes: 32 additions & 0 deletions packages/performant-ui/src/components/Badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import clsx from 'clsx';
import React from 'react';

const colors = {
faircopy: 'bg-[#E9ECF5] hover:bg-[#CED8E9] text-[#324872]',
green: 'bg-green-100 hover:bg-green-200 text-green-500',
gray: 'bg-gray-100 hover:bg-gray-200 text-gray-500',
red: 'bg-red-100 hover:bg-red-200 text-red-500',
blue: 'bg-blue-100 hover:bg-blue-200 text-blue-500'
} as const;

interface Props {
className?: string
children: React.ReactNode
color?: keyof typeof colors
}

const Badge: React.FC<Props> = (props) => {
const color = props.color || 'faircopy';

return (
<span className={clsx(
'py-1 px-1.5 rounded-md text-xs font-semibold',
colors[color],
props.className
)}>
{props.children}
</span>
);
};

export default Badge;
51 changes: 51 additions & 0 deletions packages/performant-ui/src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import clsx from 'clsx';
import React, { useMemo } from 'react';

interface Props {
ariaLabel?: string
className?: string
children?: React.ReactNode
disabled?: boolean
iconOnly?: boolean
onClick?: (...args: any[]) => any
size?: 'xs' | 's' | 'base' | 'l' | 'xl'
type?: 'button' | 'submit' | 'reset'
variant?: 'filled' | 'outline' | 'plain'
}

const Button: React.FC<Props> = (props) => {
const size = useMemo(() => props.size || 'base', [props.size]);
const variant = useMemo(() => props.variant || 'filled', [props.variant]);

return (
<button
aria-label={props.ariaLabel}
className={clsx(
'font-semibold rounded-md outline-offset-2 focus:outline-2 focus:outline-primary disabled:opacity-50 hover:cursor-pointer',
{
'bg-primary text-white hover:brightness-110': variant === 'filled',
'bg-white border border-zinc-200 hover:bg-zinc-200': variant === 'outline',
'hover:bg-black/10': variant === 'plain',
'py-1 px-1': size === 'xs' && props.iconOnly,
'py-1.5 px-1.5': size === 's' && props.iconOnly,
'py-2 px-2': size === 'base' && props.iconOnly,
'py-2.5 px-2.5': size === 'l' && props.iconOnly,
'py-3 px-3': size === 'xl' && props.iconOnly,
'py-1 px-2 text-xs': size === 'xs' && !props.iconOnly,
'py-1 px-2 text-sm': size === 's' && !props.iconOnly,
'py-1.5 px-2.5 text-sm': size === 'base' && !props.iconOnly,
'py-2 px-3 text-sm': size === 'l' && !props.iconOnly,
'py-2 px-4 text-base': size === 'xl' && !props.iconOnly
},
props.className
)}
disabled={props.disabled}
onClick={props.onClick}
type={props.type || 'button'}
>
{props.children}
</button>
);
};

export default Button;
Loading