Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions packages/core/src/components/Grid/GridCellHueHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
updateHueName,
} from "@core/stores/colors";
import { $bgColorModeLeft } from "@core/stores/settings";
import { HueAngle, HueName, type HueId } from "@core/types";
import type { HueId } from "@core/types";
import type { AnyProps } from "@core/utils/react/types";

import { DATA_ATTR_CELL_HUE_ID } from "./constants";
Expand Down Expand Up @@ -101,7 +101,7 @@ const NameInput = memo(function NameInput({ hueId }: HueComponentProps) {
/>
)
}
onChange={(e) => updateHueName(hueId, HueName(e.target.value))}
onChange={(e) => updateHueName(hueId, e.target.value)}
/>
);
});
Expand All @@ -120,14 +120,13 @@ const AngleInput = memo(function AngleInput({ hueId }: HueComponentProps) {
kind="ghost"
label={LABEL_HUE}
placeholder={PLACEHOLDER_HUE}
inputMode="numeric"
min={HUE_MIN_ANGLE}
max={HUE_MAX_ANGLE}
value={angle}
title={HINT_DEGREE}
error={error}
onChange={(e) =>
updateHueAngle(hueId, HueAngle(e.target.value ? Number.parseFloat(e.target.value) : 0))
}
onChange={(e) => updateHueAngle(hueId, e.target.value)}
/>
);
});
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/components/Grid/GridCellLevelAdd.module.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
.cell {
composes: gridColumnHeader from "./Grid.module.css";
align-items: flex-start;
justify-content: flex-start;
}

.button {
.addLevel {
inline-size: 100%;
padding: calc(var(--spacing) * 4.5) calc(var(--spacing) * 2);
margin-block-start: calc(var(--spacing) * 9);
padding: calc(var(--spacing) * 4.75) calc(var(--spacing) * 2);
}

.removeCaps {
margin-block-start: calc(var(--spacing) * 6.75);
}
18 changes: 16 additions & 2 deletions packages/core/src/components/Grid/GridCellLevelAdd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,40 @@ import { useSubscribe } from "@spred/react";

import { Button } from "@core/components/Button/Button";
import { LPlus } from "@core/components/Icon/LPlus";
import { insertLevel } from "@core/stores/colors";
import { $isAnyChromaCapSet, insertLevel, resetAllChroma } from "@core/stores/colors";
import { $bgColorModeRight } from "@core/stores/settings";

import { XsArrowBackCross } from "../Icon/XsArrowBackCross";

import { GridCell } from "./GridCell";
import styles from "./GridCellLevelAdd.module.css";

export const GridCellLevelAdd = memo(function GridCellLevelAdd() {
const bgMode = useSubscribe($bgColorModeRight);
const isAnyChromaCapSet = useSubscribe($isAnyChromaCapSet);
const handleClick = useCallback(() => insertLevel(), []);

return (
<GridCell bgColor="right" bgMode={bgMode} className={styles.cell}>
<Button
className={styles.button}
className={styles.addLevel}
kind="bordered"
size="l"
icon={<LPlus />}
onClick={handleClick}
aria-label="Insert new level row at the end"
/>
{isAnyChromaCapSet && (
<Button
className={styles.removeCaps}
kind="primary"
size="xs"
iconStart={<XsArrowBackCross />}
onClick={resetAllChroma}
>
All caps
</Button>
)}
</GridCell>
);
});
77 changes: 47 additions & 30 deletions packages/core/src/components/Grid/GridCellLevelHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useCallback, useRef } from "react";
import { type CSSProperties, memo, useCallback, useRef } from "react";

import { useSignal, useSubscribe } from "@spred/react";
import clsx from "clsx";
Expand All @@ -11,12 +11,18 @@ import { withNumericIncrementControls } from "@core/components/Input/enhancers/w
import { withValidation } from "@core/components/Input/enhancers/withValidation";
import { Input } from "@core/components/Input/Input";
import { useAppEvent } from "@core/hooks/useFocusRefOnEvent";
import { CONTRAST_MIN, getContrastMaxLevel, getContrastStep } from "@core/schemas/color";
import {
CHROMA_MAX,
CHROMA_MIN,
CONTRAST_MIN,
getContrastMaxLevel,
getContrastStep,
} from "@core/schemas/color";
import {
$levelIds,
getLevel,
insertLevel,
updateLevelChroma,
updateLevelchromaCap,
updateLevelContrast,
updateLevelName,
} from "@core/stores/colors";
Expand All @@ -29,7 +35,7 @@ import {
contrastModelStore,
directionModeStore,
} from "@core/stores/settings";
import { LevelChroma, LevelContrast, LevelName, type LevelId } from "@core/types";
import type { LevelId } from "@core/types";
import { formatOklch } from "@core/utils/colors/formatOklch";
import type { AnyProps } from "@core/utils/react/types";

Expand All @@ -41,10 +47,11 @@ import styles from "./GridCellLevelHeader.module.css";
type LevelComponentProps<P extends AnyProps = {}> = { levelId: LevelId } & P;

const LABEL_NAME = "Level name";
const LABEL_CHROMA_CAP_SET = "Set cap";
const LABEL_CHROMA_CAP_DEFINED = "Cap";
const LABEL_CHROMA = "Level chroma";
const PLACEHOLDER_LEVEL = "Level";
const PLACEHOLDER_CONTRAST = "CR";
const PLACEHOLDER_CHROMA = "Chroma";

const HINT_LEVEL = "Color level name";
const HINT_FG_TO_BG_CONTRAST = "Contrast of text color to the background";
Expand Down Expand Up @@ -101,7 +108,7 @@ const NameInput = memo(function NameInput({ levelId }: LevelComponentProps) {
value={name}
title={HINT_LEVEL}
error={error}
onChange={(e) => updateLevelName(levelId, LevelName(e.target.value))}
onChange={(e) => updateLevelName(levelId, e.target.value)}
/>
);
});
Expand All @@ -125,7 +132,6 @@ const ContrastInput = memo(function ContrastInput({
return (
<LevelContrastInput
id={`level-contrast-${levelId}`}
className={styles.inputPrimary}
size="xl"
kind="bordered"
customization={
Expand All @@ -140,56 +146,67 @@ const ContrastInput = memo(function ContrastInput({
"--input-bg-color": tintColor.css,
}
}
inputMode={contrastModel === "apca" ? "numeric" : "decimal"}
min={CONTRAST_MIN}
max={getContrastMaxLevel(contrastModel)}
step={getContrastStep(contrastModel)}
label={contrastModel.toUpperCase()}
showLabel="always"
placeholder={PLACEHOLDER_CONTRAST}
value={contrast}
title={directionMode === "fgToBg" ? HINT_FG_TO_BG_CONTRAST : HINT_BG_TO_FG_CONTRAST}
error={error}
onChange={(e) => {
let newValue = Number.parseFloat(e.target.value);
if (Number.isNaN(newValue)) {
newValue = 0;
}
updateLevelContrast(levelId, LevelContrast(newValue));
}}
onChange={(e) => updateLevelContrast(levelId, e.target.value)}
/>
);
});

const LevelChromaInput = withValidation(withNumericIncrementControls(withAutosize(Input)));
const CHROMA_INPUT_PRECISION = 3;
const LevelChromaInput = withValidation(withNumericIncrementControls(Input));
const ChromaInput = memo(function ChromaInput({ levelId }: LevelComponentProps) {
const level = getLevel(levelId);
const $chroma = useSignal((get) => {
const chroma = useSubscribe(level.chroma.$raw);
const chromaCap = useSubscribe(level.chromaCap.$raw);
const error = useSubscribe(level.chromaCap.$validationError);
const $chromaPlaceholder = useSignal((get) => {
return get(chromaModeStore.$lastValidValue) === "even"
? get(level.$tintColor).referencedC.toFixed(2)
? get(level.chroma.$lastValidValue).toFixed(CHROMA_INPUT_PRECISION)
: "max";
});
const chroma = useSubscribe($chroma);
const error = useSubscribe(level.chroma.$validationError);
const $chromaLabel = useSignal((get) => {
const chromaCap = get(level.chromaCap.$raw);

return chromaCap ? LABEL_CHROMA_CAP_DEFINED : LABEL_CHROMA_CAP_SET;
});
const chromaLabel = useSubscribe($chromaLabel);
const chromaPlaceholder = useSubscribe($chromaPlaceholder);

return (
<LevelChromaInput
id={`level-chroma-${levelId}`}
className={styles.inputSecondary}
size="m"
kind="ghost"
label={LABEL_CHROMA}
placeholder={PLACEHOLDER_CHROMA}
inputMode="decimal"
value={chroma}
style={
{
"--input-label-color": "var(--color-secondary)",
"--input-label-fw": 400,
} as CSSProperties
}
min={CHROMA_MIN}
max={CHROMA_MAX}
precision={CHROMA_INPUT_PRECISION}
baseValue={chroma}
step={0.001}
label={chromaLabel}
aria-label={LABEL_CHROMA}
showLabel={chromaCap ? "always" : "hover"}
placeholder={chromaPlaceholder}
value={chromaCap ?? ""}
title={HINT_CHROMA}
error={error}
disabled
onChange={(e) => {
let newValue = Number.parseFloat(e.target.value);
if (Number.isNaN(newValue)) {
newValue = 0;
}
updateLevelChroma(levelId, LevelChroma(newValue));
}}
onChange={(e) => updateLevelchromaCap(levelId, e.target.value)}
/>
);
});
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/components/Grid/GridLeftTopCell.module.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.cell {
composes: gridTopLeftCell from "./Grid.module.css";
gap: calc(var(--spacing) * 4.5);
gap: calc(var(--spacing) * 3.75);
align-items: flex-start;
justify-content: flex-start;
}
Expand All @@ -18,7 +18,6 @@

.bottom {
translate: 0 -1px;
margin-top: auto;
}

.contrastModelButton {
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/components/Grid/GridLeftTopCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,6 @@ export const GridLeftTopCell = memo(function GridLeftTopCell() {
</div>

<div className={clsx(styles.container, styles.bottom)}>
<Select
size="xs"
value={chromaModeValue}
options={CHROMA_OPTIONS}
onChange={(e) => updateChromaMode(parseChromaMode(e.target.value))}
title={LABEL_CHROMA_MODE}
/>
<div className={clsx(styles.container, styles.colorModePicker)}>
<Button
size="xs"
Expand All @@ -86,6 +79,13 @@ export const GridLeftTopCell = memo(function GridLeftTopCell() {
colors
</Text>
</div>
<Select
size="xs"
value={chromaModeValue}
options={CHROMA_OPTIONS}
onChange={(e) => updateChromaMode(parseChromaMode(e.target.value))}
title={LABEL_CHROMA_MODE}
/>
</div>
</GridCell>
);
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/components/Icon/XsArrowBackCross.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { SVGProps } from "react";

export type XsArrowBackProps = SVGProps<SVGSVGElement>;

export function XsArrowBackCross(props: XsArrowBackProps) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" {...props}>
<path
fill="currentColor"
d="M8.875 4.125c0-.5523-.4478-1-1-1H4.5527a1 1 0 0 0-.832.4453l-1.25 1.875a1 1 0 0 0 0 1.1094l1.25 1.875a1 1 0 0 0 .832.4453H7.875c.5522 0 1-.4477 1-1v-3.75Zm-1.0186.7256L6.7265 6l1.13 1.1494-.713.7012-1.124-1.1436-1.169 1.1494-.7011-.7128 1.1699-1.1504-1.123-1.1426.7128-.7012 1.1172 1.1367 1.1172-1.1367.713.7012ZM9.875 7.875c0 1.1046-.8955 2-2 2H4.5527a2 2 0 0 1-1.664-.8906l-1.25-1.875a2 2 0 0 1 0-2.2188l1.25-1.875a2 2 0 0 1 1.664-.8906H7.875c1.1045 0 2 .8954 2 2v3.75Z"
/>
</svg>
);
}
26 changes: 20 additions & 6 deletions packages/core/src/components/Input/Input.module.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.container {
display: inline-flex;
flex-direction: column;
gap: calc(var(--spacing) / 2);
gap: var(--spacing);

inline-size: 100%;
max-inline-size: 100%;
Expand Down Expand Up @@ -60,20 +60,28 @@
.label {
cursor: text;

font-family: var(--font-sans);
font-size: var(--text-ui-s);
font-weight: 500;
font-weight: var(--input-label-fw, 500);
line-height: var(--text-ui-s--line-height);
color: var(--input-label-color, var(--input-color));
text-align: center;

.kind_ghost & {
visibility: hidden;
}

.kind_ghost:focus-within &[data-visible],
.kind_ghost:hover &[data-visible],
.kind_ghost &[data-visible="always"] {
visibility: visible;
}
}

.inputContainer {
display: flex;
gap: var(--spacing);
align-items: center;

:where(.size_xl) & {
inline-size: 100%;
}
}

.slot {
Expand All @@ -84,13 +92,19 @@
.input {
inline-size: 100%;
max-inline-size: var(--input-max-inline-size, 100%);

font-size: max(var(--min-font-size), var(--text-m));
font-weight: 500;
line-height: max(var(--min-font-size), 1.25rem);

:where(.size_xl) & {
inline-size: 100%;
font-size: var(--text-ui-l);
line-height: var(--text-ui-l--line-height);
}

&.fullWidth {
inline-size: 100%;
text-align: center;
}

Expand Down
Loading