Use nested css
This commit is contained in:
parent
ad844be552
commit
d004ba56fd
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@codex-storage/marketplace-ui-components",
|
||||
"version": "0.0.27",
|
||||
"version": "0.0.28",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@codex-storage/marketplace-ui-components",
|
||||
"version": "0.0.27",
|
||||
"version": "0.0.28",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lucide-react": "^0.453.0"
|
||||
|
@ -41,6 +41,7 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"@codex-storage/sdk-js": ">=0.0.12",
|
||||
"postcss-nesting": "^13.0.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
}
|
||||
|
@ -628,6 +629,50 @@
|
|||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/selector-resolve-nested": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz",
|
||||
"integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss-selector-parser": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/selector-specificity": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
|
||||
"integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss-selector-parser": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
|
||||
|
@ -3770,6 +3815,18 @@
|
|||
"integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
|
@ -5558,7 +5615,6 @@
|
|||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
@ -5824,8 +5880,7 @@
|
|||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
|
@ -5939,7 +5994,6 @@
|
|||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -5964,6 +6018,46 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-nesting": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz",
|
||||
"integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@csstools/selector-resolve-nested": "^3.0.0",
|
||||
"@csstools/selector-specificity": "^5.0.0",
|
||||
"postcss-selector-parser": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
|
||||
"integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
|
@ -6673,7 +6767,6 @@
|
|||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
@ -7134,8 +7227,7 @@
|
|||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"type": "git",
|
||||
"url": "https://github.com/codex-storage/codex-marketplace-ui-components"
|
||||
},
|
||||
"version": "0.0.27",
|
||||
"version": "0.0.28",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"prepack": "npm run build",
|
||||
|
@ -37,7 +37,8 @@
|
|||
"peerDependencies": {
|
||||
"@codex-storage/sdk-js": ">=0.0.12",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
"react-dom": "^18.3.1",
|
||||
"postcss-nesting": "^13.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^2.0.2",
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import "./alert.css";
|
||||
import { CSSProperties, ReactNode } from "react";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-color-primary"?: string;
|
||||
"--codex-color-warning"?: string;
|
||||
"--codex-font-family"?: string;
|
||||
}
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
variant: "success" | "warning" | "toast";
|
||||
|
@ -20,21 +13,11 @@ type Props = {
|
|||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-border-radius
|
||||
* --codex-color-primary: string;
|
||||
* --codex-color-warning
|
||||
* --codex-font-family
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
Icon?: ReactNode;
|
||||
};
|
||||
|
||||
export function Alert({
|
||||
variant,
|
||||
style,
|
||||
title,
|
||||
Icon,
|
||||
children,
|
||||
|
@ -42,20 +25,16 @@ export function Alert({
|
|||
...rest
|
||||
}: Props) {
|
||||
return (
|
||||
<div
|
||||
className={`alert alert--${variant} ${className}`}
|
||||
style={style}
|
||||
{...rest}
|
||||
>
|
||||
<div className={`alert alert--${variant} ${className}`} {...rest}>
|
||||
{Icon && (
|
||||
<span className="alert-circleIcon">
|
||||
<span className="alert-icon">{Icon}</span>
|
||||
<span>
|
||||
<span>{Icon}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="alert-body">
|
||||
<b className="alert-title">{title}</b>
|
||||
<div className="alert-message">{children}</div>
|
||||
<div>
|
||||
<b>{title}</b>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -9,15 +9,14 @@
|
|||
word-break: break-word;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
b {
|
||||
text-transform: capitalize;
|
||||
margin-bottom: 0.25rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
display: flex;
|
||||
width: 1rem;
|
||||
color: rgb(var(--codex-alert-color));
|
||||
}
|
||||
|
||||
.alert-circleIcon {
|
||||
span {
|
||||
background: rgba(var(--codex-alert-color), 0.2);
|
||||
border-radius: 75px;
|
||||
height: 1.75rem;
|
||||
|
@ -25,18 +24,19 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
width: 1rem;
|
||||
color: rgb(var(--codex-alert-color));
|
||||
}
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
text-transform: capitalize;
|
||||
margin-bottom: 0.25rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.alert--success {
|
||||
&.alert--success {
|
||||
--codex-alert-color: var(--codex-color-success);
|
||||
}
|
||||
|
||||
.alert--warning {
|
||||
&.alert--warning {
|
||||
--codex-alert-color: var(--codex-color-warning);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
backdrop-filter: blur(2px);
|
||||
display: block;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.backdrop[aria-expanded] {
|
||||
&[aria-expanded] {
|
||||
z-index: 10;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.document-noOverflow {
|
||||
overflow: hidden;
|
||||
|
|
|
@ -1,20 +1,7 @@
|
|||
import { ComponentType, CSSProperties } from "react";
|
||||
import { ComponentType } from "react";
|
||||
import "./button.css";
|
||||
import { attributes } from "../utils/attributes";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-color-primary"?: string;
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-border-color"?: string;
|
||||
"--codex-font-family"?: string;
|
||||
"--codex-color-on-primary"?: string;
|
||||
"--codex-color-disabled"?: string;
|
||||
"--codex-color-outline"?: string;
|
||||
"--codex-button-loader"?: string;
|
||||
"--codex-button-background-busy"?: string;
|
||||
"--codex-button-color-box-shadow"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* Button style variant. Default is primary.
|
||||
|
@ -50,21 +37,6 @@ type Props = {
|
|||
* Apply custom classname.
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-color-primary
|
||||
* --codex-border-radius
|
||||
* --codex-border-color
|
||||
* --codex-font-family
|
||||
* --codex-color-on-primary: The colors when the button is primary
|
||||
* --codex-color-disabled: The disabled background color
|
||||
* --codex-color-outline: The color when the button is outline
|
||||
* --codex-button-loader: The url svg image for the spinner
|
||||
* --codex-button-background-busy: The background color image when the button is busy
|
||||
* --codex-button-color-box-shadow: The shadow color on focus
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
};
|
||||
|
||||
export function Button({
|
||||
|
@ -75,7 +47,6 @@ export function Button({
|
|||
onMouseLeave,
|
||||
fetching = false,
|
||||
disabled = false,
|
||||
style,
|
||||
variant = "primary",
|
||||
onClick,
|
||||
}: Props) {
|
||||
|
@ -84,7 +55,6 @@ export function Button({
|
|||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
style={style}
|
||||
className={`button ${className} button--${variant}`}
|
||||
{...attributes({
|
||||
disabled: disabled || fetching,
|
||||
|
@ -93,11 +63,11 @@ export function Button({
|
|||
})}
|
||||
>
|
||||
{Icon && (
|
||||
<div className="button-icon">
|
||||
<div>
|
||||
<Icon />
|
||||
</div>
|
||||
)}
|
||||
<span className="button-label">{label}</span>
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,51 +14,12 @@
|
|||
font-family: var(--codex-font-family);
|
||||
border: 1px solid transparent;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
.button--primary {
|
||||
background-color: var(--codex-color-primary);
|
||||
color: var(--codex-color-on-primary);
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.button--primary:disabled {
|
||||
background-color: var(--codex-color-disabled);
|
||||
}
|
||||
|
||||
.button--outline:disabled {
|
||||
color: var(--codex-color-disabled);
|
||||
}
|
||||
|
||||
.button-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.button--outline {
|
||||
color: var(--codex-color-outline, var(--codex-color-contrast));
|
||||
border-color: var(--codex-border-color);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.button[aria-busy] {
|
||||
&[aria-busy] {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.button[aria-busy]::after {
|
||||
&[aria-busy]::after {
|
||||
content: " ";
|
||||
display: block;
|
||||
background-image: var(
|
||||
|
@ -85,13 +46,52 @@
|
|||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.button--primary:not(:disabled):hover {
|
||||
&.button--primary:not(:disabled):hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 0 3px
|
||||
var(--codex-button-color-box-shadow, var(--codex-color-primary-variant));
|
||||
}
|
||||
|
||||
.button--outline:not(:disabled):hover {
|
||||
&.button--outline:not(:disabled):hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 0 2px var(--codex-border-color);
|
||||
}
|
||||
|
||||
&.button--primary {
|
||||
background-color: var(--codex-color-primary);
|
||||
color: var(--codex-color-on-primary);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&.button--primary:disabled {
|
||||
background-color: var(--codex-color-disabled);
|
||||
}
|
||||
|
||||
&.button--outline:disabled {
|
||||
color: var(--codex-color-disabled);
|
||||
}
|
||||
|
||||
&.button--outline {
|
||||
color: var(--codex-color-outline, var(--codex-color-contrast));
|
||||
border-color: var(--codex-border-color);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,7 @@
|
|||
import {
|
||||
AnimationEventHandler,
|
||||
ComponentType,
|
||||
CSSProperties,
|
||||
useState,
|
||||
} from "react";
|
||||
import { AnimationEventHandler, ComponentType, useState } from "react";
|
||||
import "./buttonIcon.css";
|
||||
import { attributes } from "../utils/attributes";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-button-icon-background"?: string;
|
||||
"--codex-border-color"?: string;
|
||||
"--codex-color-disabled"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
Icon: ComponentType<{
|
||||
className?: string;
|
||||
|
@ -29,14 +18,6 @@ type Props = {
|
|||
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-button-icon-background
|
||||
* --codex-border-color
|
||||
* --codex-color-disabled
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
/**
|
||||
* Apply custom classname.
|
||||
*/
|
||||
|
@ -51,7 +32,6 @@ type Props = {
|
|||
export function ButtonIcon({
|
||||
Icon,
|
||||
onClick,
|
||||
style,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
className = "",
|
||||
|
@ -74,7 +54,6 @@ export function ButtonIcon({
|
|||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={onInternalClick}
|
||||
style={style}
|
||||
{...attributes({ disabled: disabled, "aria-disabled": disabled })}
|
||||
>
|
||||
<Icon className={animationClassName} onAnimationEnd={onAnimationEnd} />
|
||||
|
|
|
@ -10,33 +10,28 @@
|
|||
cursor: pointer;
|
||||
transition: box-shadow 0.35s;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.buttonIcon--big {
|
||||
&.buttonIcon--big {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.buttonIcon--small {
|
||||
&.buttonIcon--small {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.buttonIcon svg {
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
|
||||
.buttonIcon:not(:disabled):hover {
|
||||
&:not(:disabled):hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 0 2px var(--codex-border-color);
|
||||
}
|
||||
|
||||
.buttonIcon:disabled {
|
||||
&:disabled {
|
||||
color: var(--codex-color-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.buttonIcon--buzz {
|
||||
&.buttonIcon--buzz {
|
||||
-webkit-animation-name: buzz;
|
||||
animation-name: buzz;
|
||||
-webkit-animation-duration: 0.45s;
|
||||
|
@ -47,6 +42,22 @@
|
|||
animation-iteration-count: 5;
|
||||
}
|
||||
|
||||
&.buttonIcon--bounce {
|
||||
-webkit-animation-name: bounce;
|
||||
animation-name: bounce;
|
||||
-webkit-animation-duration: 0.6s;
|
||||
animation-duration: 0.6s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
animation-timing-function: linear;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
svg {
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes buzz {
|
||||
0% {
|
||||
-webkit-transform: translateX(0) rotate(0deg);
|
||||
|
@ -74,17 +85,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.buttonIcon--bounce {
|
||||
-webkit-animation-name: bounce;
|
||||
animation-name: bounce;
|
||||
-webkit-animation-duration: 0.6s;
|
||||
animation-duration: 0.6s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
animation-timing-function: linear;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0% {
|
||||
-webkit-transform: translateY(0) scale(1);
|
||||
|
|
|
@ -1,33 +1,19 @@
|
|||
import { CSSProperties, ReactNode } from "react";
|
||||
import { ReactNode } from "react";
|
||||
import "./card.css";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-border-color"?: string;
|
||||
"--codex-font-family"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
|
||||
className?: string;
|
||||
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-border-radius
|
||||
* --codex-border-color
|
||||
* --codex-font-family
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
};
|
||||
|
||||
export function Card({ children, style, className = "", title }: Props) {
|
||||
export function Card({ children, className = "", title }: Props) {
|
||||
return (
|
||||
<div className={`card ${className}`} style={style}>
|
||||
<div className="card-header">{title}</div>
|
||||
<div className="card-body">{children}</div>
|
||||
<div className={`card ${className}`}>
|
||||
<header>{title}</header>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
border: 1px solid var(--codex-border-color);
|
||||
font-family: var(--codex-font-family);
|
||||
background-color: var(--codex-background-secondary);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
header {
|
||||
border-bottom: 1px solid var(--codex-border-color);
|
||||
padding: 1rem 1.5rem;
|
||||
font-size: 1.15rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
div {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,9 @@ import {
|
|||
import "./dropdown.css";
|
||||
import { attributes } from "../utils/attributes";
|
||||
import { Backdrop } from "../Backdrop/Backdrop";
|
||||
import { Input, InputCustomStyleCSS } from "../Input/Input";
|
||||
import { Input } from "../Input/Input";
|
||||
import { classnames } from "../utils/classnames";
|
||||
|
||||
interface CustomStyleCSS extends InputCustomStyleCSS {
|
||||
"--codex-dropdown-panel-background"?: string;
|
||||
"--codex-dropdown-border"?: string;
|
||||
"--codex-dropdown-option-background-hover"?: string;
|
||||
}
|
||||
|
||||
export type DropdownOption = {
|
||||
/**
|
||||
* Dropdown option icon displayed on the left
|
||||
|
@ -70,14 +64,6 @@ type Props = {
|
|||
|
||||
onMouseLeave?: () => void;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-dropdown-panel-background
|
||||
* --codex-dropdown-border
|
||||
* --codex-dropdown-option-background-hover
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
label: string;
|
||||
|
||||
id: string;
|
||||
|
@ -85,7 +71,6 @@ type Props = {
|
|||
|
||||
export function Dropdown({
|
||||
placeholder,
|
||||
style,
|
||||
options,
|
||||
label,
|
||||
id,
|
||||
|
@ -134,12 +119,10 @@ export function Dropdown({
|
|||
const attr = attributes({ "aria-expanded": focused });
|
||||
|
||||
return (
|
||||
<>
|
||||
<label className="dropdown-label" htmlFor={id}>
|
||||
{label}
|
||||
</label>
|
||||
<div className={"dropdown " + className}>
|
||||
<label htmlFor={id}>{label}</label>
|
||||
|
||||
<div className={`dropdown ${className}`} style={style}>
|
||||
<div>
|
||||
<Backdrop onClose={onClose} open={focused} />
|
||||
|
||||
<Input
|
||||
|
@ -157,28 +140,20 @@ export function Dropdown({
|
|||
id={id}
|
||||
/>
|
||||
|
||||
<div className="dropdown-panel" {...attr}>
|
||||
<ul className="dropdown-panel" {...attr}>
|
||||
{filtered.length ? (
|
||||
filtered.map((o) => (
|
||||
<div
|
||||
className="dropdown-option"
|
||||
onClick={() => onSelect(o)}
|
||||
key={o.title}
|
||||
>
|
||||
<li onClick={() => onSelect(o)} key={o.title}>
|
||||
{o.Icon && <o.Icon />}
|
||||
<div>
|
||||
<span className="dropdown-title">{o.title}</span>
|
||||
{o.subtitle && (
|
||||
<span className="dropdown-subtitle">{o.subtitle}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<span>{o.title}</span>
|
||||
{o.subtitle && <span>{o.subtitle}</span>}
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<p className="dropdown-noResults">No results found</p>
|
||||
<p>No results found</p>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
.dropdown {
|
||||
label {
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
color: var(--codex-color);
|
||||
}
|
||||
|
||||
> div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-panel {
|
||||
ul {
|
||||
position: absolute;
|
||||
padding: 0.5rem;
|
||||
background-color: var(
|
||||
|
@ -22,34 +30,14 @@
|
|||
max-height: 20rem;
|
||||
overflow-y: auto;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.dropdown-label {
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
color: var(--codex-color);
|
||||
}
|
||||
|
||||
.dropdown-panel[aria-expanded] {
|
||||
transform: translateY(0.5rem);
|
||||
&[aria-expanded] {
|
||||
transform: translateY(0rem);
|
||||
opacity: 1;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.dropdown-input {
|
||||
position: relative;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M8.35355 4.06066C8.15829 3.8654 7.84171 3.8654 7.64645 4.06066L5.35355 6.35355C5.15829 6.54882 4.84171 6.54882 4.64645 6.35355C4.45118 6.15829 4.45118 5.84171 4.64645 5.64645L6.93934 3.35356C7.52513 2.76777 8.47487 2.76777 9.06066 3.35355L11.3536 5.64645C11.5488 5.84171 11.5488 6.15829 11.3536 6.35355C11.1583 6.54882 10.8417 6.54882 10.6464 6.35355L8.35355 4.06066Z' fill='%236b7280'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M8.35355 11.9393C8.15829 12.1346 7.84171 12.1346 7.64645 11.9393L5.35355 9.64645C5.15829 9.45119 4.84171 9.45119 4.64645 9.64645C4.45118 9.84171 4.45118 10.1583 4.64645 10.3536L6.93934 12.6464C7.52513 13.2322 8.47487 13.2322 9.06066 12.6464L11.3536 10.3536C11.5488 10.1583 11.5488 9.84171 11.3536 9.64645C11.1583 9.45119 10.8417 9.45119 10.6464 9.64645L8.35355 11.9393Z' fill='%236b7280'/%3E%3C/svg%3E%0A");
|
||||
background-position: right 0.5rem center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.25em 1.25em;
|
||||
}
|
||||
|
||||
.dropdown-input:focus {
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.dropdown-option {
|
||||
li {
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--codex-border-radius);
|
||||
transition: background-color 0.35s;
|
||||
|
@ -57,29 +45,39 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.dropdown-option:hover {
|
||||
&:hover {
|
||||
background-color: var(
|
||||
--codex-dropdown-option-background-hover,
|
||||
var(--codex-background-light)
|
||||
);
|
||||
}
|
||||
|
||||
.dropdown-noResults {
|
||||
padding: 0.75rem 0.25rem;
|
||||
}
|
||||
|
||||
.dropdown-title {
|
||||
span {
|
||||
display: block;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.dropdown-subtitle {
|
||||
span + span {
|
||||
mix-blend-mode: difference;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.dropdown-title,
|
||||
.dropdown-subtitle {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
position: relative;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M8.35355 4.06066C8.15829 3.8654 7.84171 3.8654 7.64645 4.06066L5.35355 6.35355C5.15829 6.54882 4.84171 6.54882 4.64645 6.35355C4.45118 6.15829 4.45118 5.84171 4.64645 5.64645L6.93934 3.35356C7.52513 2.76777 8.47487 2.76777 9.06066 3.35355L11.3536 5.64645C11.5488 5.84171 11.5488 6.15829 11.3536 6.35355C11.1583 6.54882 10.8417 6.54882 10.6464 6.35355L8.35355 4.06066Z' fill='%236b7280'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M8.35355 11.9393C8.15829 12.1346 7.84171 12.1346 7.64645 11.9393L5.35355 9.64645C5.15829 9.45119 4.84171 9.45119 4.64645 9.64645C4.45118 9.84171 4.45118 10.1583 4.64645 10.3536L6.93934 12.6464C7.52513 13.2322 8.47487 13.2322 9.06066 12.6464L11.3536 10.3536C11.5488 10.1583 11.5488 9.84171 11.3536 9.64645C11.1583 9.45119 10.8417 9.45119 10.6464 9.64645L8.35355 11.9393Z' fill='%236b7280'/%3E%3C/svg%3E%0A");
|
||||
background-position: right 0.5rem center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.25em 1.25em;
|
||||
|
||||
&:focus {
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0.75rem 0.25rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import "./failure.css";
|
||||
import { Button } from "../Button/Button";
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-code-font-size"?: string;
|
||||
"--codex-text-contrast"?: string;
|
||||
"--codex-font-family"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
|
@ -29,14 +22,6 @@ type Props = {
|
|||
* The button label
|
||||
*/
|
||||
button?: string;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-code-font-size
|
||||
* --codex-text-contrast
|
||||
* --codex-font-family
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
};
|
||||
|
||||
export function Failure({
|
||||
|
@ -48,9 +33,9 @@ export function Failure({
|
|||
}: Props) {
|
||||
return (
|
||||
<div className="failure">
|
||||
<h1 className="failure-code">{code}</h1>
|
||||
<h2 className="failure-title">{title}</h2>
|
||||
<div className="failure-message">{message}</div>
|
||||
<h1>{code}</h1>
|
||||
<h2>{title}</h2>
|
||||
<div>{message}</div>
|
||||
{onClick && <Button label={button} onClick={onClick} />}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
.failure-code {
|
||||
.failure {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
h1 {
|
||||
font-size: var(--codex-code-font-size, 6rem);
|
||||
line-height: 1;
|
||||
color: var(--codex-text-contrast);
|
||||
|
@ -6,15 +13,7 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.failure {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.failure-title {
|
||||
h2 {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
font-weight: 600;
|
||||
|
@ -22,7 +21,8 @@
|
|||
color: var(--codex-text-contrast);
|
||||
}
|
||||
|
||||
.failure-message {
|
||||
div {
|
||||
margin-bottom: 0.75rem;
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
ChangeEvent,
|
||||
ComponentType,
|
||||
CSSProperties,
|
||||
forwardRef,
|
||||
InputHTMLAttributes,
|
||||
useState,
|
||||
|
@ -9,16 +8,6 @@ import {
|
|||
import { attributes } from "../utils/attributes";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import "./input.css";
|
||||
import { SimpleText } from "../SimpleText/SimpleText";
|
||||
|
||||
export interface InputCustomStyleCSS extends CSSProperties {
|
||||
"--codex-input-background"?: string;
|
||||
"--codex-color"?: string;
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-input-border"?: string;
|
||||
"--codex-color-primary"?: string;
|
||||
"--codex-input-background-disabled"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
|
@ -80,54 +69,39 @@ export const Input = forwardRef<HTMLInputElement, Props>(
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{label && (
|
||||
<label className="input-label" htmlFor={id}>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={classnames(
|
||||
["input-icon", !!Icon],
|
||||
[inputContainerClassName]
|
||||
["input"],
|
||||
["input--invalid", invalid || isInvalid],
|
||||
["input--icon", !!Icon],
|
||||
[inputClassName || ""]
|
||||
)}
|
||||
>
|
||||
{label && <label htmlFor={id}>{label}</label>}
|
||||
|
||||
<div className={classnames([inputContainerClassName])}>
|
||||
{Icon && (
|
||||
<div className="input-iconElement">
|
||||
<div>
|
||||
<Icon />
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
id={id}
|
||||
ref={ref}
|
||||
className={classnames(
|
||||
["input"],
|
||||
["input--invalid", invalid || isInvalid],
|
||||
["input-icon-input", !!Icon],
|
||||
[inputClassName || ""]
|
||||
)}
|
||||
className={classnames([inputClassName || ""])}
|
||||
onChange={onInternalChange}
|
||||
style={style}
|
||||
{...attributes({
|
||||
disabled,
|
||||
"aria-disabled": disabled,
|
||||
"aria-invalid": invalid || isInvalid,
|
||||
})}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{helper && (
|
||||
<div>
|
||||
<SimpleText
|
||||
className="input-helper-text"
|
||||
variant={invalid || isInvalid ? "error" : "light"}
|
||||
>
|
||||
{helper}
|
||||
</SimpleText>
|
||||
{helper && <small>{helper}</small>}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.input {
|
||||
input {
|
||||
background-color: var(--codex-input-background);
|
||||
color: white;
|
||||
border-radius: var(--codex-border-radius);
|
||||
|
@ -10,14 +11,15 @@
|
|||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
height: 64px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input--invalid {
|
||||
&.input--invalid input {
|
||||
color: var(--codex-input-color-error);
|
||||
border-color: var(--codex-input-color-error);
|
||||
}
|
||||
|
||||
.input-label {
|
||||
label {
|
||||
margin-bottom: 0.75rem;
|
||||
display: block;
|
||||
font-family: var(--codex-input-font-family, var(--codex-font-family));
|
||||
|
@ -29,77 +31,41 @@
|
|||
color: var(--codex-input-label-color);
|
||||
}
|
||||
|
||||
.input:not(.input[disabled]):active,
|
||||
.input:not(.input[disabled]):focus {
|
||||
& input:not(.input[disabled]):active,
|
||||
& input:not(.input[disabled]):focus {
|
||||
box-shadow: 0 0 0 1px var(--codex-border-color);
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
position: relative;
|
||||
& input[disabled] {
|
||||
background-color: var(--codex-input-background-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.input-iconElement {
|
||||
&.input--icon input {
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
|
||||
> div {
|
||||
position: relative;
|
||||
|
||||
div {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
padding: 0 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.input-icon-input {
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
|
||||
.input[disabled] {
|
||||
background-color: var(--codex-input-background-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.input-floating {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input-spacing {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.input-floating-label {
|
||||
position: absolute;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
left: 1rem;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition:
|
||||
top 0.15s,
|
||||
font-size 0.15s;
|
||||
}
|
||||
|
||||
.input-floating-input {
|
||||
padding-top: 1.75rem;
|
||||
}
|
||||
|
||||
.input.input-floating-input:not(:placeholder-shown) ~ .input-floating-label,
|
||||
.input.input-floating-input:focus ~ .input-floating-label {
|
||||
top: -25px;
|
||||
font-size: 0.875rem;
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
|
||||
.input-helper-text {
|
||||
small {
|
||||
margin-top: 0.25rem;
|
||||
display: inline-block;
|
||||
font-size: 0.9rem;
|
||||
color: var(--codex-input-label-color);
|
||||
}
|
||||
|
||||
/* @media (min-width: 801px) {
|
||||
.input {
|
||||
min-width: 20rem;
|
||||
&.input--invalid small {
|
||||
color: var(--codex-color-error-hexa);
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import { ChangeEvent, CSSProperties, LegacyRef, ReactNode } from "react";
|
||||
import { ChangeEvent, LegacyRef, ReactNode } from "react";
|
||||
import "./inputGroup.css";
|
||||
import { Input } from "../Input/Input";
|
||||
import { Select } from "../Select/Select";
|
||||
|
||||
export interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-border-color"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
label: string;
|
||||
|
||||
|
@ -63,13 +58,6 @@ type Props = {
|
|||
|
||||
step?: string;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-border-radius
|
||||
* --codex-border-color
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
|
@ -91,7 +79,6 @@ export function InputGroup({
|
|||
name,
|
||||
helper,
|
||||
type = "text",
|
||||
style,
|
||||
group,
|
||||
className = "",
|
||||
inputClassName = "",
|
||||
|
@ -111,17 +98,17 @@ export function InputGroup({
|
|||
extra,
|
||||
}: Props) {
|
||||
return (
|
||||
<div className={`inputGroup ${className}`} style={style}>
|
||||
<div className="inputGroup-container">
|
||||
<div className="inputGroup-element">
|
||||
<div className="inputGroup-inputContainer">
|
||||
<div className={`inputGroup input-group ${className}`}>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
id={id}
|
||||
name={name}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
inputClassName={"inputGroup-input " + inputClassName}
|
||||
inputClassName={inputClassName}
|
||||
type={type}
|
||||
value={value}
|
||||
step={step}
|
||||
|
@ -149,12 +136,10 @@ export function InputGroup({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="inputGroup-helpers">
|
||||
<span>
|
||||
{helper && <small className="inputGroup-helper">{helper}</small>}
|
||||
</span>
|
||||
<p>
|
||||
<span>{helper && <small>{helper}</small>}</span>
|
||||
{extra}
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
.inputGroup-element {
|
||||
.input-group {
|
||||
> div {
|
||||
flex-grow: 1;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.inputGroup-container {
|
||||
flex-grow: 1;
|
||||
}
|
||||
> div:first-child {
|
||||
flex: 1;
|
||||
|
||||
.inputGroup-helper {
|
||||
height: 15px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input.inputGroup-input {
|
||||
input {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
select.inputGroup-select {
|
||||
select {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
min-width: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
.inputGroup-unit {
|
||||
> div:nth-child(2) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--codex-border-color);
|
||||
|
@ -33,13 +31,18 @@ select.inputGroup-select {
|
|||
background-color: var(--codex-border-color);
|
||||
padding: calc(0.5rem + 0.5px);
|
||||
}
|
||||
|
||||
.inputGroup-inputContainer {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.inputGroup-helpers {
|
||||
p {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 0;
|
||||
|
||||
small {
|
||||
height: 15px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ type Props = {
|
|||
disableActionButton?: boolean;
|
||||
|
||||
children: ReactNode;
|
||||
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function Modal({
|
||||
|
@ -60,6 +62,7 @@ export function Modal({
|
|||
onClose,
|
||||
disableActionButton,
|
||||
disableCloseButton,
|
||||
className = "",
|
||||
displayCloseButton = true,
|
||||
displayActionButton = false,
|
||||
labelActionButton = "Action",
|
||||
|
@ -79,24 +82,21 @@ export function Modal({
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Backdrop open={internalOpen} onClose={internalClose} />
|
||||
|
||||
<div
|
||||
className={classnames(
|
||||
["modal"],
|
||||
["modal--internalOpen", internalOpen],
|
||||
["modal--open", open]
|
||||
["modal--open", open],
|
||||
["modal--actions", !!onAction],
|
||||
[className]
|
||||
)}
|
||||
>
|
||||
<div className="modal-body">{open && children}</div>
|
||||
<Backdrop open={internalOpen} onClose={internalClose} />
|
||||
|
||||
<div
|
||||
className={classnames(
|
||||
["modal-buttons--between", !!onAction],
|
||||
["modal-buttons--center", !onAction]
|
||||
)}
|
||||
>
|
||||
<dialog>
|
||||
<main>{open && children}</main>
|
||||
|
||||
<footer>
|
||||
{displayCloseButton && (
|
||||
<Button
|
||||
label={labelCloseButton}
|
||||
|
@ -113,8 +113,8 @@ export function Modal({
|
|||
disabled={disableActionButton}
|
||||
/>
|
||||
)}
|
||||
</footer>
|
||||
</dialog>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.modal {
|
||||
dialog {
|
||||
transition:
|
||||
transform 0.25s,
|
||||
opacity 0.25s;
|
||||
|
@ -17,48 +18,38 @@
|
|||
background-color: var(--codex-background);
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--codex-border-radius);
|
||||
border: none;
|
||||
width: calc(100% - 6rem);
|
||||
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
min-width: 500px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal--internalOpen {
|
||||
&.modal--internalOpen dialog {
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.modal--open {
|
||||
&.modal--open dialog {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.modal-buttons--center {
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-buttons--between {
|
||||
&.modal--actions footer {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
.modal {
|
||||
min-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.modal {
|
||||
width: calc(100% - 6rem);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { classnames } from "../utils/classnames";
|
||||
import "./networkIndicator.css";
|
||||
|
||||
type Props = {
|
||||
online: boolean;
|
||||
|
||||
text: string;
|
||||
};
|
||||
|
||||
export function NetworkIndicator({ online, text }: Props) {
|
||||
return (
|
||||
<div className="networkIndicator">
|
||||
<div
|
||||
className={classnames(
|
||||
["networkIndicator-point"],
|
||||
["networkIndicator-point--online", online],
|
||||
["networkIndicator-point--offline", !online]
|
||||
)}
|
||||
></div>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
.networkIndicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.networkIndicator-point {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
animation-duration: 3s;
|
||||
animation-name: flash;
|
||||
animation-iteration-count: infinite;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.networkIndicator-point--online {
|
||||
background-color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.networkIndicator-point--offline {
|
||||
background-color: rgb(217, 53, 38);
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
40% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import { ReactNode } from "react";
|
||||
import { Button } from "../Button/Button";
|
||||
import "./placeholder.css";
|
||||
import { SimpleText } from "../SimpleText/SimpleText";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
|
@ -35,14 +34,10 @@ export function Placeholder({
|
|||
}: Props) {
|
||||
return (
|
||||
<div className={"placeholder " + className}>
|
||||
<div className="placeholder-icon">{Icon}</div>
|
||||
<b className="placeholder-title">{title}</b>
|
||||
<div>{Icon}</div>
|
||||
<b>{title}</b>
|
||||
|
||||
{subtitle && (
|
||||
<div className="placeholder-subtitle">
|
||||
<SimpleText variant="light">{subtitle}</SimpleText>
|
||||
</div>
|
||||
)}
|
||||
{subtitle && <p>subtitle</p>}
|
||||
|
||||
<div className="placeholder-message">{message} </div>
|
||||
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
.placeholder {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
> div:first-child {
|
||||
display: block;
|
||||
margin: auto auto 0.75rem;
|
||||
}
|
||||
|
||||
.placeholder-title {
|
||||
b {
|
||||
margin-bottom: 0.25rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.placeholder-subtitle {
|
||||
p {
|
||||
margin-bottom: 0.25rem;
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
color: var(--codex-input-label-color);
|
||||
}
|
||||
|
||||
.placeholder-button {
|
||||
.button {
|
||||
margin: 0.75rem auto 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
import { ChangeEvent, CSSProperties } from "react";
|
||||
import { ChangeEvent } from "react";
|
||||
import "./select.css";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-select-background"?: string;
|
||||
"--codex-color"?: string;
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-select-border"?: string;
|
||||
"--codex-select-icon-url"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
label: string;
|
||||
|
||||
|
@ -33,11 +25,6 @@ type Props = {
|
|||
|
||||
onMouseLeave?: () => void;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
defaultValue?: string;
|
||||
|
||||
value: string;
|
||||
|
@ -54,26 +41,20 @@ export function Select({
|
|||
onFocus,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
style,
|
||||
className = "",
|
||||
defaultValue,
|
||||
value,
|
||||
}: Props) {
|
||||
return (
|
||||
<>
|
||||
<label htmlFor={id} className="select-label">
|
||||
{label}
|
||||
</label>
|
||||
<div>
|
||||
<div className={"select " + className}>
|
||||
<label htmlFor={id}>{label}</label>
|
||||
<select
|
||||
id={id}
|
||||
className={`select ${className}`}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
onFocus={onFocus}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
style={style}
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
>
|
||||
|
@ -84,6 +65,5 @@ export function Select({
|
|||
))}
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.select {
|
||||
select {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
|
@ -22,23 +23,24 @@
|
|||
background-repeat: no-repeat;
|
||||
background-size: 1.25em 1.25em;
|
||||
box-sizing: border-box;
|
||||
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
min-width: 20rem;
|
||||
}
|
||||
}
|
||||
|
||||
.select:hover,
|
||||
.select:focus-visible,
|
||||
.select:active {
|
||||
&:hover,
|
||||
&:focus-visible,
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1px var(--codex-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.select-label {
|
||||
label {
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
color: var(--codex-color);
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
.select {
|
||||
min-width: 20rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,17 +16,10 @@ export function Sheets({ open, onClose, children }: Props) {
|
|||
const attr = attributes({ "aria-expanded": open });
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
["sheets-container"],
|
||||
["sheets-container--open", open]
|
||||
)}
|
||||
>
|
||||
<Backdrop onClose={onClose} open={open} className={"sheets-backdrop"} />
|
||||
<div className={classnames(["sheets"], ["sheets--open", open])}>
|
||||
<Backdrop onClose={onClose} open={open} />
|
||||
|
||||
<div className="sheets" {...attr}>
|
||||
{children}
|
||||
</div>
|
||||
<aside {...attr}>{children}</aside>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,30 +1,24 @@
|
|||
.sheets {
|
||||
position: fixed;
|
||||
transition: transform 0.25s;
|
||||
background-color: var(--codex-background-secondary);
|
||||
z-index: 2;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sheets-container {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.sheets-container--open {
|
||||
&.sheets--open {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.sheets-backdrop {
|
||||
z-index: -1;
|
||||
}
|
||||
aside {
|
||||
position: fixed;
|
||||
transition: transform 0.25s;
|
||||
background-color: var(--codex-background-secondary);
|
||||
z-index: 2;
|
||||
justify-content: space-between;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.sheets {
|
||||
& {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
bottom: 0;
|
||||
|
@ -33,14 +27,14 @@
|
|||
right: 0;
|
||||
}
|
||||
|
||||
.sheets[aria-expanded] {
|
||||
&[aria-expanded] {
|
||||
transform: translatex(0);
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 999px) {
|
||||
.sheets {
|
||||
& {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
bottom: 0;
|
||||
|
@ -50,8 +44,10 @@
|
|||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.sheets[aria-expanded] {
|
||||
&[aria-expanded] {
|
||||
transform: translatey(0);
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import { CSSProperties, ReactNode } from "react";
|
||||
import "./simpleText.css";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-color"?: string;
|
||||
"--codex-color-primary"?: string;
|
||||
"--codex-color-light"?: string;
|
||||
"--codex-color-error"?: string;
|
||||
"--codex-color-warning"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* Default variant is normal
|
||||
*/
|
||||
variant?: "normal" | "primary" | "light" | "error" | "warning";
|
||||
|
||||
className?: string;
|
||||
|
||||
children: string | ReactNode;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-color
|
||||
* --codex-color-primary
|
||||
* --codex-color-light
|
||||
* --codex-color-error
|
||||
* --codex-color-warning
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
size?: "normal" | "small";
|
||||
|
||||
center?: boolean;
|
||||
|
||||
bold?: boolean;
|
||||
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export function SimpleText({
|
||||
variant = "normal",
|
||||
className = "",
|
||||
center,
|
||||
size = "normal",
|
||||
onClick,
|
||||
style,
|
||||
children,
|
||||
bold,
|
||||
}: Props) {
|
||||
const c = `text text--${variant} ${className} ${center ? "text--center" : ""} ${bold ? "text--bold" : ""}`;
|
||||
|
||||
if (size === "small") {
|
||||
return (
|
||||
<small onClick={onClick} className={c} style={style}>
|
||||
{children}
|
||||
</small>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span onClick={onClick} className={c} style={style}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
.text--normal {
|
||||
color: var(--codex-color);
|
||||
}
|
||||
|
||||
.text--primary {
|
||||
color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.text--light {
|
||||
color: var(--codex-input-label-color);
|
||||
}
|
||||
|
||||
.text--center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text--error {
|
||||
color: rgb(var(--codex-color-error));
|
||||
}
|
||||
|
||||
.text--warning {
|
||||
color: rgb(var(--codex-color-warning));
|
||||
}
|
||||
|
||||
.text--bold {
|
||||
font-weight: bold;
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import { SimpleText } from "../SimpleText/SimpleText";
|
||||
import { PrettyBytes } from "../utils/bytes";
|
||||
import "./spaceAllocation.css";
|
||||
|
||||
|
@ -25,36 +24,31 @@ export function SpaceAllocation({ data }: Props) {
|
|||
const total = data.reduce((acc, val) => acc + val.size, 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="nodeSpaceAllocation-bar">
|
||||
<div className="space-allocation">
|
||||
<header>
|
||||
{data.map((d) => (
|
||||
<span
|
||||
key={d.title}
|
||||
className={`nodeSpaceAllocation-barItem nodeSpaceAllocation-barQuota ${d.className || ""}`}
|
||||
className={`${d.className || ""}`}
|
||||
style={{
|
||||
width: (d.size / total) * 100 + "%",
|
||||
backgroundColor: d.color,
|
||||
}}
|
||||
></span>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="nodeSpaceAllocation-legend">
|
||||
<ul className="nodeSpaceAllocation-legend">
|
||||
{data.map((d) => (
|
||||
<div key={d.title} className={"nodeSpaceAllocation-legendRow"}>
|
||||
<div
|
||||
className={`nodeSpaceAllocation-legendItem nodeSpaceAllocation-quota`}
|
||||
style={{ backgroundColor: d.color }}
|
||||
></div>
|
||||
<div className="nodeSpaceAllocation-legendItem-text">
|
||||
<small>{d.title}</small>
|
||||
<SimpleText variant="light" size="small">
|
||||
{PrettyBytes(d.size)}
|
||||
</SimpleText>
|
||||
</div>
|
||||
</div>
|
||||
<li key={d.title}>
|
||||
<span style={{ backgroundColor: d.color }}></span>
|
||||
<p>
|
||||
<span>{d.title}</span>
|
||||
<small> {PrettyBytes(d.size)}</small>
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,48 +1,46 @@
|
|||
.nodeSpaceAllocation-bar {
|
||||
.space-allocation {
|
||||
header {
|
||||
height: 10px;
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nodeSpaceAllocation-barQuota {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.nodeSpaceAllocation-barItem {
|
||||
span {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.nodeSpaceAllocation-barQuota-used {
|
||||
border-top-left-radius: var(--codex-border-radius);
|
||||
border-bottom-left-radius: var(--codex-border-radius);
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.nodeSpaceAllocation-legend {
|
||||
ul {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
padding-left: 0;
|
||||
|
||||
.nodeSpaceAllocation-legendItem {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
|
||||
> span {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.nodeSpaceAllocation-legendRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.nodeSpaceAllocation-legendItem-text {
|
||||
p {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1rem;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
|
||||
small {
|
||||
color: var(--codex-input-label-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
.step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: opacity 0.35s;
|
||||
|
||||
&:not([disabled]):not(.step--active):hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
&:not(:last-child) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
> div:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: opacity 0.35s;
|
||||
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.35s;
|
||||
background-color: var(
|
||||
--codex-stepper-background,
|
||||
var(--codex-background-light)
|
||||
);
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
}
|
||||
|
||||
&.step--active > div:first-child,
|
||||
&.step--done > div:first-child {
|
||||
background-color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
div:nth-child(2) {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
place-items: center;
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
flex: 1;
|
||||
background-color: var(
|
||||
--codex-stepper-background,
|
||||
var(--codex-background-light)
|
||||
);
|
||||
position: relative;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
& {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: var(--codex-color-primary);
|
||||
height: 1px;
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
animation-duration: 1s;
|
||||
animation-name: step-back;
|
||||
animation-fill-mode: forwards;
|
||||
opacity: 0;
|
||||
/* animation-direction: reverse; */
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
font-size: 14px;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
& {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.step--done {
|
||||
div:nth-child(2) {
|
||||
&::before {
|
||||
background-color: var(--codex-color-primary);
|
||||
display: inline-block;
|
||||
animation-duration: 1s;
|
||||
animation-name: step;
|
||||
animation-fill-mode: forwards;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.step--mounted {
|
||||
div:nth-child(2) {
|
||||
&::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import { Check } from "lucide-react";
|
|||
import { useEffect, useRef } from "react";
|
||||
import { attributes } from "../utils/attributes";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import "./Step.css";
|
||||
|
||||
type StepProps = {
|
||||
/**
|
||||
|
@ -52,36 +53,22 @@ export function Step({
|
|||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
["stepper-step", true],
|
||||
["stepper-step-active", isActive]
|
||||
["step", true],
|
||||
["step--active", isActive],
|
||||
["step--done", isDone],
|
||||
["step--,mounted", mounted.current]
|
||||
)}
|
||||
onClick={() => onClick?.(step)}
|
||||
{...attributes({ disabled: !onClick })}
|
||||
>
|
||||
<div className="stepper-step-info">
|
||||
<div
|
||||
className={classnames(
|
||||
["stepper-number", true],
|
||||
["stepper-number-active", isActive],
|
||||
["stepper-number-done", isDone]
|
||||
)}
|
||||
>
|
||||
<span className="stepper-numberValue">
|
||||
{isDone ? <Check size={"1.25rem"} /> : step + 1}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>{isDone ? <Check size={"1.25rem"} /> : step + 1}</span>
|
||||
</div>
|
||||
|
||||
{!isLast && (
|
||||
<div className="stepper-step-between">
|
||||
<div
|
||||
className={classnames(
|
||||
["stepper-separator", true],
|
||||
["stepper-separator-done", isDone],
|
||||
["stepper-separator-mounted", mounted.current]
|
||||
)}
|
||||
></div>
|
||||
<span className={"stepper-text"}>{title}</span>
|
||||
<div>
|
||||
<hr />
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
import { Button } from "../Button/Button";
|
||||
import "./stepper.css";
|
||||
import { CSSProperties, Dispatch, ReactNode } from "react";
|
||||
import { Dispatch, ReactNode } from "react";
|
||||
import { Spinner } from "../Spinner/Spinner";
|
||||
import { Step } from "./Step";
|
||||
import { StepperAction, StepperState } from "./useStepperReducer";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-background"?: string;
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-stepper-background": string;
|
||||
"--codex-color-primary": string;
|
||||
"--codex-border-color": string;
|
||||
}
|
||||
import { classnames } from "../utils/classnames";
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
|
@ -24,8 +17,6 @@ type Props = {
|
|||
*/
|
||||
children: ReactNode;
|
||||
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
/**
|
||||
* The duration between steps
|
||||
*/
|
||||
|
@ -69,7 +60,6 @@ export function Stepper({
|
|||
titles,
|
||||
children,
|
||||
state,
|
||||
style,
|
||||
dispatch,
|
||||
className = "",
|
||||
backLabel = "Back",
|
||||
|
@ -95,8 +85,13 @@ export function Stepper({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={"stepper " + className} style={style}>
|
||||
<div className="stepper-steps">
|
||||
<div
|
||||
className={classnames(
|
||||
["stepper " + className],
|
||||
["stepper--progress", state.progress]
|
||||
)}
|
||||
>
|
||||
<header>
|
||||
{titles.map((title, index) => (
|
||||
<Step
|
||||
title={title}
|
||||
|
@ -108,19 +103,13 @@ export function Stepper({
|
|||
onClick={state.step > index ? () => onChangeStep(index) : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="stepper-body">
|
||||
{state.progress ? (
|
||||
<div className="stepper-progress">
|
||||
<Spinner width={"3rem"} />
|
||||
</div>
|
||||
) : (
|
||||
<>{children}</>
|
||||
)}
|
||||
</div>
|
||||
<main>
|
||||
{state.progress ? <Spinner width={"3rem"} /> : <>{children}</>}
|
||||
</main>
|
||||
|
||||
<div className="stepper-buttons">
|
||||
<footer>
|
||||
<Button
|
||||
label={backLabel}
|
||||
variant="outline"
|
||||
|
@ -132,7 +121,7 @@ export function Stepper({
|
|||
onClick={() => onChangeStep(state.step + 1)}
|
||||
disabled={!state.isNextEnable}
|
||||
/>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,65 +3,15 @@
|
|||
flex-direction: column;
|
||||
background-color: var(--codex-background);
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.stepper-progress,
|
||||
.stepper-step-info,
|
||||
.stepper-steps,
|
||||
.stepper-step {
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: opacity 0.35s;
|
||||
}
|
||||
|
||||
.stepper-step:not([disabled]):not(.stepper-separator-active):hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.stepper-number {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.35s;
|
||||
}
|
||||
|
||||
.stepper-number:not(.stepper-number-active):not(.stepper-number-done) {
|
||||
background-color: var(
|
||||
--codex-stepper-background,
|
||||
var(--codex-background-light)
|
||||
);
|
||||
}
|
||||
|
||||
.stepper-separator {
|
||||
height: 1px;
|
||||
flex: 1;
|
||||
background-color: var(
|
||||
--codex-stepper-background,
|
||||
var(--codex-background-light)
|
||||
);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stepper-number-done,
|
||||
.stepper-number-active {
|
||||
background-color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.stepper-numberValue {
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
|
||||
.stepper-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.stepper-body {
|
||||
main {
|
||||
margin: 1.5rem 0;
|
||||
border: 1px dashed var(--codex-border-color);
|
||||
border-radius: var(--codex-border-radius);
|
||||
|
@ -73,50 +23,26 @@
|
|||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (min-width: 801px) {
|
||||
& {
|
||||
min-width: 500px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stepper-progress {
|
||||
&.stepper--progress {
|
||||
main {
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stepper-numberValue {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.stepper-separator::before {
|
||||
background-color: var(--codex-color-primary);
|
||||
height: 1px;
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
animation-duration: 1s;
|
||||
animation-name: step-back;
|
||||
animation-fill-mode: forwards;
|
||||
opacity: 0;
|
||||
/* animation-direction: reverse; */
|
||||
}
|
||||
|
||||
.stepper-separator-done::before {
|
||||
background-color: var(--codex-color-primary);
|
||||
display: inline-block;
|
||||
animation-duration: 1s;
|
||||
animation-name: step;
|
||||
animation-fill-mode: forwards;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.stepper-separator-mounted::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.stepper-success {
|
||||
footer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
place-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes step {
|
||||
|
@ -138,40 +64,3 @@
|
|||
width: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
.stepper-container {
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.stepper-step:not(:last-child) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stepper-step-between {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.stepper-text {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.stepper-body {
|
||||
min-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.stepper-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stepper-step:not(.stepper-step-active) .stepper-text,
|
||||
.stepper-separator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import { ReactNode } from "react";
|
||||
import "./cell.css";
|
||||
|
||||
export type CellProps = {
|
||||
children: ReactNode | string;
|
||||
} & React.DetailedHTMLProps<
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>,
|
||||
HTMLTableCellElement
|
||||
>;
|
||||
|
||||
export const Cell = ({ children, className = "", ...rest }: CellProps) => (
|
||||
<td className={"cell" + className} {...rest}>
|
||||
{children}
|
||||
</td>
|
||||
);
|
|
@ -1,18 +0,0 @@
|
|||
import { Fragment, ReactElement } from "react";
|
||||
import { Cell, CellProps } from "./Cell";
|
||||
import "./row.css";
|
||||
|
||||
export type RowProps = {
|
||||
cells: ReactElement<CellProps, typeof Cell>[];
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function Row({ cells, className = "" }: RowProps) {
|
||||
return (
|
||||
<tr className={"row " + className}>
|
||||
{cells.map((Cell, index) => (
|
||||
<Fragment key={index}>{Cell}</Fragment>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import "./table.css";
|
||||
import { ArrowDownUp, Search } from "lucide-react";
|
||||
import { Row, RowProps } from "./Row";
|
||||
import { Fragment, ReactElement, useEffect, useState } from "react";
|
||||
import { Fragment, ReactElement, ReactNode, useEffect, useState } from "react";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import { attributes } from "../utils/attributes";
|
||||
|
||||
export type TabSortState = "asc" | "desc" | null;
|
||||
|
||||
|
@ -61,10 +61,16 @@ export function Table({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={`table-container ${className}`}>
|
||||
<table className={"table"}>
|
||||
<thead className="table-thead">
|
||||
<tr className="table-theadTr">
|
||||
<div
|
||||
className={classnames(
|
||||
["table"],
|
||||
[className],
|
||||
["table--empty", !!rows.length]
|
||||
)}
|
||||
>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{headers.map((col, index) => {
|
||||
const [name, sort] = Array.isArray(col) ? col : [col];
|
||||
const state = index === sortSelected[0] ? sortSelected[1] : null;
|
||||
|
@ -72,9 +78,14 @@ export function Table({
|
|||
|
||||
return (
|
||||
<th
|
||||
className={classnames(
|
||||
["table-theadTh"],
|
||||
["table-theadTh--clickable", !!sort]
|
||||
{...attributes(
|
||||
sort
|
||||
? {
|
||||
role: "button",
|
||||
"aria-sort":
|
||||
state === "asc" ? "ascending" : "descending",
|
||||
}
|
||||
: {}
|
||||
)}
|
||||
key={name}
|
||||
onClick={() => {
|
||||
|
@ -82,14 +93,9 @@ export function Table({
|
|||
sort?.(nxt);
|
||||
}}
|
||||
>
|
||||
<div className="table-theadTh-content">
|
||||
<div>
|
||||
<span>{name}</span>
|
||||
{sort && (
|
||||
<ArrowDownUp
|
||||
className={"table-theadTh-icon--" + state}
|
||||
size={"1rem"}
|
||||
></ArrowDownUp>
|
||||
)}
|
||||
{sort && <ArrowDownUp size={"1rem"}></ArrowDownUp>}
|
||||
</div>
|
||||
</th>
|
||||
);
|
||||
|
@ -104,11 +110,39 @@ export function Table({
|
|||
</table>
|
||||
|
||||
{!rows.length && (
|
||||
<div className="table-placeholder">
|
||||
<div>
|
||||
<Search />
|
||||
<p className="table-placeholderText">No data.</p>
|
||||
<p>No data.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export type CellProps = {
|
||||
children: ReactNode | string;
|
||||
} & React.DetailedHTMLProps<
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>,
|
||||
HTMLTableCellElement
|
||||
>;
|
||||
|
||||
export const Cell = ({ children, className = "", ...rest }: CellProps) => (
|
||||
<td className={className} {...rest}>
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
|
||||
export type RowProps = {
|
||||
cells: ReactElement<CellProps, typeof Cell>[];
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function Row({ cells, className = "" }: RowProps) {
|
||||
return (
|
||||
<tr className={className}>
|
||||
{cells.map((Cell, index) => (
|
||||
<Fragment key={index}>{Cell}</Fragment>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
.cell {
|
||||
text-align: left;
|
||||
padding: 1rem;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
.row {
|
||||
border-bottom: 1px solid var(--codex-border-color);
|
||||
transition: background-color 0.35s;
|
||||
}
|
||||
|
||||
.row:hover {
|
||||
background-color: var(--codex-background-light);
|
||||
}
|
|
@ -1,70 +1,131 @@
|
|||
.table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-placeholder {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.table-placeholderText {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
border: 1px solid var(--codex-border-color);
|
||||
background-color: var(--codex-background-secondary);
|
||||
padding: 2rem;
|
||||
border-radius: var(--codex-border-radius);
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.table-theadTr {
|
||||
table {
|
||||
border-spacing: 0 12px;
|
||||
width: 100%;
|
||||
|
||||
thead {
|
||||
tr {
|
||||
border-bottom: 1px solid var(--codex-border-color);
|
||||
}
|
||||
border-radius: 8px;
|
||||
|
||||
.table-theadTh {
|
||||
th {
|
||||
color: var(--codex-color-light);
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
text-align: left;
|
||||
padding: 1rem;
|
||||
}
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
background-color: #232323;
|
||||
|
||||
.table-theadTh--clickable {
|
||||
&[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table-theadTh-content {
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.table-theadTh-content svg {
|
||||
svg {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table-theadTh-content svg path {
|
||||
svg path {
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.35s;
|
||||
}
|
||||
|
||||
.table-theadTh-icon--desc path:nth-child(1),
|
||||
.table-theadTh-icon--desc path:nth-child(2) {
|
||||
th[aria-sort="descending"] path:nth-child(1),
|
||||
th[aria-sort="descending"] path:nth-child(2) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.table-theadTh-icon--asc path:nth-child(3),
|
||||
.table-theadTh-icon--asc path:nth-child(4) {
|
||||
th[aria-sort="ascending"] path:nth-child(3),
|
||||
th[aria-sort="ascending"] path:nth-child(4) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
border-bottom: 1px solid var(--codex-border-color);
|
||||
transition: background-color 0.35s;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--codex-background-light);
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: left;
|
||||
height: 64px;
|
||||
box-sizing: border-box;
|
||||
background-color: #232323;
|
||||
padding: 0 16px;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.006em;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: " ";
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
display: inline-block;
|
||||
background: #96969633;
|
||||
position: absolute;
|
||||
bottom: -7px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 2rem;
|
||||
|
||||
p {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { ComponentType } from "react";
|
||||
import "./tabs.css";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import { attributes } from "../utils/attributes";
|
||||
|
||||
export type TabProps = {
|
||||
label: string;
|
||||
|
@ -26,11 +27,8 @@ export function Tabs({ tabs, onTabChange, tabIndex }: Props) {
|
|||
{tabs.map((tab, index) => (
|
||||
<div
|
||||
key={tab.label}
|
||||
className={classnames(
|
||||
["tabs-tab"],
|
||||
["tabs-tab--active", tabIndex === index],
|
||||
[tab.className || ""]
|
||||
)}
|
||||
{...attributes({ "aria-selected": tabIndex === index })}
|
||||
className={classnames([tab.className || ""])}
|
||||
onClick={() => onTabChange(index)}
|
||||
>
|
||||
{tab.Icon && <tab.Icon />}
|
||||
|
|
|
@ -3,20 +3,8 @@
|
|||
margin-top: 1rem;
|
||||
gap: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tabs-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding-bottom: 1rem;
|
||||
cursor: pointer;
|
||||
transition: 0.35s opacity;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tabs::after {
|
||||
&::after {
|
||||
width: 100%;
|
||||
background-color: var(--codex-background-light);
|
||||
content: " ";
|
||||
|
@ -26,15 +14,21 @@
|
|||
top: 31px;
|
||||
}
|
||||
|
||||
.tabs-tab:not(.files-headerTab--active) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding-bottom: 1rem;
|
||||
cursor: pointer;
|
||||
transition: 0.35s opacity;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
|
||||
.tabs-tab:hover {
|
||||
&:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.tabs-tab--active:after {
|
||||
&[aria-selected]:after {
|
||||
width: 100%;
|
||||
background-color: var(--codex-color-contrast);
|
||||
content: " ";
|
||||
|
@ -43,10 +37,5 @@
|
|||
top: 11px;
|
||||
top: 31px;
|
||||
}
|
||||
|
||||
.tabs-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
import { CSSProperties, useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { attributes } from "../utils/attributes";
|
||||
import "./toast.css";
|
||||
import { CircleCheck, CircleX, Info, X } from "lucide-react";
|
||||
import { ButtonIcon } from "../ButtonIcon/ButtonIcon";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-toast-background"?: string;
|
||||
"--codex-toast-border-color"?: string;
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-toast-color"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
message: string;
|
||||
|
||||
|
@ -30,22 +23,12 @@ type Props = {
|
|||
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* codex-toast-background
|
||||
* codex-toast-border-color
|
||||
* codex-border-radius
|
||||
* codex-toast-color
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
variant: "success" | "error" | "default";
|
||||
};
|
||||
|
||||
export function Toast({
|
||||
message,
|
||||
time,
|
||||
style,
|
||||
variant,
|
||||
className = "",
|
||||
duration = 3000,
|
||||
|
@ -84,12 +67,11 @@ export function Toast({
|
|||
<div
|
||||
className={`toast ${className} toast--${variant}`}
|
||||
{...attributes({ "aria-hidden": time == 0 || msg === "" })}
|
||||
style={style}
|
||||
>
|
||||
<Icon size="1.25rem" className="toast-icon" />
|
||||
<Icon size="1.25rem" />
|
||||
|
||||
<span>
|
||||
<b className="toast-title">{variant} ! </b>
|
||||
<b>{variant} ! </b>
|
||||
<span>{msg}</span>
|
||||
</span>
|
||||
|
||||
|
|
|
@ -18,33 +18,33 @@
|
|||
border: 1px solid rgb(var(--codex-toast-color));
|
||||
background: rgba(var(--codex-toast-color), 1);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.toast-close {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
.toast[aria-hidden] {
|
||||
&[aria-hidden] {
|
||||
transform: translateX(1000px);
|
||||
}
|
||||
|
||||
.toast-title {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.toast-icon {
|
||||
fill: rgba(var(--codex-toast-color), 0.2);
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
.toast--success {
|
||||
&.toast--success {
|
||||
--codex-toast-color: var(--codex-color-success);
|
||||
}
|
||||
|
||||
.toast--error {
|
||||
&.toast--error {
|
||||
--codex-toast-color: var(--codex-color-error);
|
||||
}
|
||||
|
||||
.toast--default {
|
||||
&.toast--default {
|
||||
--codex-toast-color: var(--codex-color-grey);
|
||||
}
|
||||
|
||||
.button {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
b {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: rgba(var(--codex-toast-color), 0.2);
|
||||
stroke: white;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { FileStack, Upload as UploadIcon } from "lucide-react";
|
||||
import { ChangeEvent, CSSProperties, DragEventHandler, useRef } from "react";
|
||||
import { ChangeEvent, DragEventHandler, useRef } from "react";
|
||||
import { attributes } from "../utils/attributes.ts";
|
||||
import "./upload.css";
|
||||
import { UploadFile } from "./UploadFile.tsx";
|
||||
|
@ -7,17 +7,6 @@ import { useUploadStategy } from "./useUploadStrategy.ts";
|
|||
import { classnames } from "../utils/classnames.ts";
|
||||
import { ButtonIcon } from "../ButtonIcon/ButtonIcon.tsx";
|
||||
import { CodexData } from "@codex-storage/sdk-js";
|
||||
import { SimpleText } from "../SimpleText/SimpleText.tsx";
|
||||
|
||||
interface CustomStyleCSS extends CSSProperties {
|
||||
"--codex-border-color"?: string;
|
||||
"--codex-border-radius"?: string;
|
||||
"--codex-upload-background"?: string;
|
||||
"--codex-color-primary"?: string;
|
||||
"--codex-color"?: string;
|
||||
"--codex-color-error"?: string;
|
||||
"--codex-color-warning"?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
|
@ -68,18 +57,6 @@ type Props = {
|
|||
*/
|
||||
// useWorker?: boolean;
|
||||
|
||||
/**
|
||||
* Apply custom css variables.
|
||||
* --codex-border-color
|
||||
* --codex-border-radius
|
||||
* --codex-upload-background
|
||||
* --codex-color-primary
|
||||
* --codex-color
|
||||
* --codex-color-error
|
||||
* --codex-color-warning
|
||||
*/
|
||||
style?: CustomStyleCSS;
|
||||
|
||||
/**
|
||||
* Success message displayed when a file is updated.
|
||||
* Default: File uploaded successfully.
|
||||
|
@ -151,17 +128,16 @@ export function Upload({
|
|||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<ButtonIcon Icon={multiple ? FileStack : UploadIcon}></ButtonIcon>
|
||||
<div className="upload-text">
|
||||
<div>
|
||||
|
||||
<p>
|
||||
<b>
|
||||
Drop your {multiple ? "file(s)" : "file"} here or{" "}
|
||||
<span className="text--primary">browse</span>
|
||||
<span>browse</span>
|
||||
</b>
|
||||
</div>
|
||||
<SimpleText size="small" variant="light" center>
|
||||
{multiple ? "Up to 10 files" : "Choose one single file"}
|
||||
</SimpleText>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<small> {multiple ? "Up to 10 files" : "Choose one single file"}</small>
|
||||
|
||||
<input
|
||||
data-testid="upload"
|
||||
type="file"
|
||||
|
@ -171,7 +147,7 @@ export function Upload({
|
|||
{...attributes({ multiple: multiple })}
|
||||
/>
|
||||
|
||||
{warning && <SimpleText variant="warning">{warning}</SimpleText>}
|
||||
{warning && <span>{warning}</span>}
|
||||
</div>
|
||||
|
||||
{files.map(({ id, file }) => (
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
.upload-file {
|
||||
&[aria-invalid] {
|
||||
--codex-upload-color: rgb(var(--codex-color-error));
|
||||
}
|
||||
|
||||
&[data-done] {
|
||||
--codex-upload-color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
> div {
|
||||
background-color: var(
|
||||
--codex-upload-background,
|
||||
var(--codex-background-secondary)
|
||||
);
|
||||
border-radius: var(--codex-border-radius);
|
||||
border: 1px solid var(--codex-border-color);
|
||||
padding: 1em 2rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
header {
|
||||
flex-grow: 1;
|
||||
gap: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> div:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-grow: 1;
|
||||
|
||||
img {
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
p {
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
|
||||
b {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--codex-upload-color);
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 150px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: var(--codex-upload-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0.25 0;
|
||||
|
||||
progress {
|
||||
flex-grow: 1;
|
||||
background-color: var(
|
||||
--codex-upload-background,
|
||||
var(--codex-background-secondary)
|
||||
);
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: none;
|
||||
border-radius: var(--codex-border-radius);
|
||||
height: 0.5rem;
|
||||
width: 100%;
|
||||
border: none;
|
||||
|
||||
&[value]::-webkit-progress-bar {
|
||||
background-color: var(--codex-background-light);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
&[value]::-webkit-progress-value {
|
||||
background: var(--codex-upload-color);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
&[value]::-moz-progress-bar {
|
||||
border-radius: 50px;
|
||||
background: var(--codex-upload-color);
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 0.85rem;
|
||||
color: var(--codex-upload-color);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import { Spinner } from "../Spinner/Spinner";
|
|||
import { CodexData } from "@codex-storage/sdk-js";
|
||||
import { WebFileIcon } from "../WebFileIcon/WebFileIcon";
|
||||
import { ButtonIcon } from "../ButtonIcon/ButtonIcon";
|
||||
import { SimpleText } from "../SimpleText/SimpleText";
|
||||
import "./UploadFile.css";
|
||||
|
||||
type UploadFileProps = {
|
||||
file: File;
|
||||
|
@ -263,37 +263,32 @@ export function UploadFile({
|
|||
const ActionIcon = () => <UploadActionIcon status={status} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={"uploadFile"}>
|
||||
<div className="uploadFile-info">
|
||||
<div className="uploadFile-infoLeft">
|
||||
{preview ? (
|
||||
<img
|
||||
src={preview}
|
||||
width="24"
|
||||
alt="Preview"
|
||||
className="uploadFile-preview"
|
||||
/>
|
||||
) : (
|
||||
<WebFileIcon type={file.type} />
|
||||
)}
|
||||
<div className="uploadFile-infoText">
|
||||
<b
|
||||
className="uploadFile-name"
|
||||
<div
|
||||
className="upload-file"
|
||||
{...attributes({
|
||||
"aria-invalid": status === "error",
|
||||
"data-done": status === "done",
|
||||
})}
|
||||
>
|
||||
<span className="uploadFile-filename">{filename}</span>
|
||||
<div>
|
||||
<header>
|
||||
<div>
|
||||
{preview ? (
|
||||
<img src={preview} width="24" alt="Preview" />
|
||||
) : (
|
||||
<WebFileIcon type={file.type} />
|
||||
)}
|
||||
<p>
|
||||
<b>
|
||||
<span>{filename}</span>
|
||||
{extension && <span>.{extension}</span>}
|
||||
</b>
|
||||
<div>
|
||||
<small>{PrettyBytes(file.size)}</small>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="uploadFile-infoRight">
|
||||
<div>
|
||||
<UploadStatusIcon status={status} />
|
||||
|
||||
<ButtonIcon
|
||||
|
@ -302,33 +297,27 @@ export function UploadFile({
|
|||
Icon={ActionIcon}
|
||||
></ButtonIcon>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="uploadFile-progress">
|
||||
<main>
|
||||
<progress
|
||||
className="uploadFile-progressBar"
|
||||
{...attributes({
|
||||
max: file ? progress.total.toString() : false,
|
||||
value: file ? progress.loaded.toString() : false,
|
||||
"aria-invalid": status === "error",
|
||||
})}
|
||||
/>
|
||||
<span className="uploadFile-progressBarPercent">
|
||||
{percent.toFixed(2)} %
|
||||
</span>
|
||||
</div>
|
||||
<span>{percent.toFixed(2)} %</span>
|
||||
</main>
|
||||
|
||||
<div className="uploadFile-message">
|
||||
<footer>
|
||||
{cid ? (
|
||||
<div className="text--primary">{successMessage}</div>
|
||||
<span>{successMessage}</span>
|
||||
) : (
|
||||
<SimpleText variant="error">
|
||||
{error ? error : <> </>}
|
||||
</SimpleText>
|
||||
<span> {error ? error : <> </>}</span>
|
||||
)}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,172 +11,24 @@
|
|||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 2rem;
|
||||
}
|
||||
|
||||
.upload-selected {
|
||||
border-color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.uploadFile {
|
||||
background-color: var(
|
||||
--codex-upload-background,
|
||||
var(--codex-background-secondary)
|
||||
);
|
||||
border-radius: var(--codex-border-radius);
|
||||
border: 1px solid var(--codex-border-color);
|
||||
padding: 1em 2rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.uploadFile-info {
|
||||
flex-grow: 1;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.upload-actions {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.upload-action-close {
|
||||
color: var(--codex-color);
|
||||
border: 1px solid var(--codex-color);
|
||||
}
|
||||
|
||||
.upload-action-confirm {
|
||||
color: var(--codex-color-primary);
|
||||
border: 1px solid var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.uploadFile-progressBar {
|
||||
flex-grow: 1;
|
||||
background-color: var(
|
||||
--codex-upload-background,
|
||||
var(--codex-background-secondary)
|
||||
);
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: none;
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.uploadFile-progressBar {
|
||||
height: 0.5rem;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.uploadFile-progressBar[value]::-webkit-progress-bar {
|
||||
background-color: var(--codex-background-light);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.uploadFile-progressBar[value]::-webkit-progress-value {
|
||||
background: var(--codex-color-primary);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.uploadFile-progressBar[value]::-moz-progress-bar {
|
||||
border-radius: 50px;
|
||||
background: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.uploadFile-progressBar[aria-invalid]::-moz-progress-bar {
|
||||
background: rgb(var(--codex-color-error));
|
||||
}
|
||||
|
||||
.uploadFile-progressBar[aria-invalid]::-webkit-progress-value {
|
||||
background: rgb(var(--codex-color-error));
|
||||
}
|
||||
|
||||
.uploadFile-filename {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 150px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.uploadFile-progressBarPercent {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.uploadFile-message {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.upload-progress-check {
|
||||
color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.upload-progress-cancelled {
|
||||
color: rgb(var(--codex-color-error));
|
||||
}
|
||||
|
||||
.uploadFile-preview {
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.uploadFile-infoLeft {
|
||||
gap: 0.5rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.uploadFile-progress {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.uploadFile-progress {
|
||||
margin: 0.25 0;
|
||||
}
|
||||
|
||||
.uploadFile-infoRight,
|
||||
.uploadFile-progress,
|
||||
.upload,
|
||||
.uploadFile-info,
|
||||
.uploadFile-infoLeft,
|
||||
.uploadFile-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uploadFile-name[aria-invalid] {
|
||||
color: rgb(var(--codex-color-error));
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
margin-top: 12px;
|
||||
line-height: 4px;
|
||||
|
||||
.uploadFile-name[data-done] {
|
||||
span {
|
||||
color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.uploadFile-infoText {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.uploadFile-infoRight {
|
||||
justify-content: space-around;
|
||||
gap: 0.25rem;
|
||||
small {
|
||||
color: var(--codex-input-label-color);
|
||||
}
|
||||
|
||||
.uploadFile-progressBarPercent,
|
||||
.uploadFile-right {
|
||||
width: 5rem;
|
||||
input + span {
|
||||
color: var(--codex-color-warning);
|
||||
}
|
||||
|
||||
.uploadFile-cid {
|
||||
transition: color 0.35s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.uploadFile-cid:hover {
|
||||
color: var(--codex-color-primary);
|
||||
}
|
||||
|
||||
.upload-warning {
|
||||
border-color: rgb(var(--codex-color-warning));
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ export { Button } from "./components/Button/Button";
|
|||
export { ButtonIcon } from "./components/ButtonIcon/ButtonIcon";
|
||||
export { Input } from "./components/Input/Input";
|
||||
export { InputGroup } from "./components/InputGroup/InputGroup";
|
||||
export { SimpleText } from "./components/SimpleText/SimpleText";
|
||||
export { Upload } from "./components/Upload/Upload";
|
||||
export { Card } from "./components/Card/Card";
|
||||
export { Select } from "./components/Select/Select";
|
||||
|
@ -16,10 +15,7 @@ export { Spinner } from "./components/Spinner/Spinner";
|
|||
export { WebFileIcon } from "./components/WebFileIcon/WebFileIcon";
|
||||
export { Stepper } from "./components/Stepper/Stepper";
|
||||
export { Backdrop } from "./components/Backdrop/Backdrop";
|
||||
export { Cell, type CellProps } from "./components/Table/Cell";
|
||||
export { Table, type TabSortState } from "./components/Table/Table";
|
||||
export { Row, type RowProps } from "./components/Table/Row";
|
||||
export { NetworkIndicator } from "./components/NetworkIndicator/NetworkIndicator";
|
||||
export * from "./components/Table/Table";
|
||||
export { Tooltip } from "./components/Tooltip/Tooltip";
|
||||
export { Collapse } from "./components/Collapse/Collapse";
|
||||
export { Placeholder } from "./components/Placeholder/Placeholder";
|
||||
|
|
|
@ -65,7 +65,12 @@ const ActionTemplate = (props: Props) => {
|
|||
return (
|
||||
<div style={{ padding: "6rem" }}>
|
||||
<button onClick={onOpen}>Make Modal</button>
|
||||
<Modal onClose={onClose} open={open} onAction={onAction}>
|
||||
<Modal
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
onAction={onAction}
|
||||
displayActionButton={true}
|
||||
>
|
||||
<p>Hello world</p>
|
||||
</Modal>
|
||||
</div>
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { NetworkIndicator } from "../src/components/NetworkIndicator/NetworkIndicator";
|
||||
|
||||
const meta = {
|
||||
title: "Components/NetworkIndicator",
|
||||
component: NetworkIndicator,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
argTypes: {},
|
||||
} satisfies Meta<typeof NetworkIndicator>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Online: Story = {
|
||||
args: {
|
||||
online: true,
|
||||
text: "Online",
|
||||
},
|
||||
};
|
||||
|
||||
export const Offline: Story = {
|
||||
args: {
|
||||
online: false,
|
||||
text: "Offline",
|
||||
},
|
||||
};
|
|
@ -20,14 +20,17 @@ export const Default: Story = {
|
|||
{
|
||||
title: "Space allocated",
|
||||
size: 10000000,
|
||||
color: "red"
|
||||
},
|
||||
{
|
||||
title: "New space allocation",
|
||||
size: 10000000 * 0.2,
|
||||
color: "yellow"
|
||||
},
|
||||
{
|
||||
title: "Remaining space",
|
||||
size: 10000000 * 0.2,
|
||||
color: "green"
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { Table } from "../src/components/Table/Table";
|
||||
import { Table, Row } from "../src/components/Table/Table";
|
||||
import "./Table.stories.css";
|
||||
import { Row } from "../src/components/Table/Row";
|
||||
import { Cell } from "../src";
|
||||
|
||||
const meta = {
|
||||
|
|
Loading…
Reference in New Issue