change slider to radio

This commit is contained in:
Ryan Di
2025-06-06 00:31:35 +10:00
parent c210b7b092
commit df1f9281b4
5 changed files with 118 additions and 81 deletions

View File

@ -128,6 +128,8 @@ import {
ArrowheadCrowfootIcon,
ArrowheadCrowfootOneIcon,
ArrowheadCrowfootOneOrManyIcon,
strokeWidthFixedIcon,
strokeWidthVariableIcon,
} from "../components/icons";
import { Fonts } from "../fonts";
@ -671,8 +673,8 @@ export const actionChangeStrokeStyle = register({
});
export const actionChangePressureSensitivity = register({
name: "changePressureSensitivity",
label: "labels.pressureSensitivity",
name: "changeStrokeType",
label: "labels.strokeType",
trackEvent: false,
perform: (elements, appState, value) => {
return {
@ -686,7 +688,47 @@ export const actionChangePressureSensitivity = register({
return null;
}
return <PressureSensitivityRange updateData={updateData} app={app} />;
const selectedElements = app.scene.getSelectedElements(app.state);
const firstElement = selectedElements.find(isFreeDrawElement);
const commonPressureSensitivity = selectedElements
.filter(isFreeDrawElement)
.reduce((acc, element) => {
const sensitivity = element.pressureSensitivity ?? 1;
if (acc !== null && acc !== sensitivity) {
return null; // No common value
}
return sensitivity;
}, firstElement?.pressureSensitivity ?? null);
const currentValue =
commonPressureSensitivity ?? appState.currentItemPressureSensitivity;
return (
<fieldset>
<legend>{t("labels.strokeType")}</legend>
<div className="buttonList">
<RadioSelection
group="pressure-sensitivity"
options={[
{
value: 0,
text: t("labels.strokeWidthFixed"),
icon: strokeWidthFixedIcon,
testId: "pressure-fixed",
},
{
value: 1,
text: t("labels.strokeWidthVariable"),
icon: strokeWidthVariableIcon,
testId: "pressure-variable",
},
]}
value={currentValue}
onChange={(value) => updateData(value)}
/>
</div>
</fieldset>
);
},
});
@ -1878,78 +1920,3 @@ export const actionChangeArrowType = register({
);
},
});
const PressureSensitivityRange = ({
updateData,
app,
}: {
updateData: (value: number) => void;
app: AppClassProperties;
}) => {
const rangeRef = useRef<HTMLInputElement>(null);
const valueRef = useRef<HTMLDivElement>(null);
const selectedElements = app.scene.getSelectedElements(app.state);
let hasCommonPressureSensitivity = true;
const firstElement = selectedElements.find(isFreeDrawElement);
const leastCommonPressureSensitivity = selectedElements
.filter(isFreeDrawElement)
.reduce((acc, element) => {
const sensitivity = element.pressureSensitivity ?? 1;
if (acc != null && acc !== sensitivity) {
hasCommonPressureSensitivity = false;
}
if (acc == null || acc > sensitivity) {
return sensitivity;
}
return acc;
}, firstElement?.pressureSensitivity ?? null);
const value = Math.round(
(leastCommonPressureSensitivity ??
app.state.currentItemPressureSensitivity) * 100,
);
useEffect(() => {
if (rangeRef.current && valueRef.current) {
const rangeElement = rangeRef.current;
const valueElement = valueRef.current;
const inputWidth = rangeElement.offsetWidth;
const thumbWidth = 15;
const position =
(value / 100) * (inputWidth - thumbWidth) + thumbWidth / 2;
valueElement.style.left = `${position}px`;
rangeElement.style.background = `linear-gradient(to right, var(--color-slider-track) 0%, var(--color-slider-track) ${value}%, var(--button-bg) ${value}%, var(--button-bg) 100%)`;
}
}, [value]);
return (
<label className="control-label">
{t("labels.pressureSensitivity")}
<div className="range-wrapper">
<input
style={{
["--color-slider-track" as string]: hasCommonPressureSensitivity
? undefined
: "var(--button-bg)",
}}
ref={rangeRef}
type="range"
min="0"
max="100"
step="10"
onChange={(event) => {
updateData(+event.target.value / 100);
}}
value={value}
className="range-input"
data-testid="pressure-sensitivity"
/>
<div className="value-bubble" ref={valueRef}>
{value !== 0 ? value : null}
</div>
<div className="zero-label">0</div>
</div>
</label>
);
};

View File

@ -69,7 +69,7 @@ export type ActionName =
| "changeStrokeStyle"
| "changeArrowhead"
| "changeArrowType"
| "changePressureSensitivity"
| "changeStrokeType"
| "changeOpacity"
| "changeFontSize"
| "toggleCanvasMenu"

View File

@ -172,7 +172,7 @@ export const SelectedShapeActions = ({
targetElements.some((element) => element.type === "freedraw")) && (
<>
{renderAction("changeStrokeShape")}
{renderAction("changePressureSensitivity")}
{renderAction("changeStrokeType")}
</>
)}

View File

@ -2269,3 +2269,71 @@ export const elementLinkIcon = createIcon(
</g>,
tablerIconProps,
);
export const strokeWidthFixedIcon = createIcon(
<g>
<path
d="M4 12 C 5 8, 6 8, 8 12"
fill="none"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8 12 C 9 16, 10 16, 12 12"
fill="none"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M12 12 C 14 8, 15 8, 16 12"
fill="none"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16 12 C 17 16, 18 16, 19 12"
fill="none"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>,
tablerIconProps,
);
export const strokeWidthVariableIcon = createIcon(
<g>
<path
d="M4 12 C 5 8, 6 8, 8 12"
fill="none"
strokeWidth="3.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8 12 C 9 16, 10 16, 12 12"
fill="none"
strokeWidth="2.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M12 12 C 14 8, 15 8, 16 12"
fill="none"
strokeWidth="2.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16 12 C 17 16, 18 16, 19 12"
fill="none"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>,
tablerIconProps,
);

View File

@ -32,7 +32,9 @@
"strokeStyle_dotted": "Dotted",
"sloppiness": "Sloppiness",
"opacity": "Opacity",
"pressureSensitivity": "Stroke sensitivity",
"strokeType": "Stroke Type",
"strokeWidthFixed": "Fixed width",
"strokeWidthVariable": "Variable width",
"textAlign": "Text align",
"edges": "Edges",
"sharp": "Sharp",