+ `;
+};
+ThreeHandlesOrdered.args = {
+ tickStep: 10,
+};
+
+// This is a very complex example from an actual application.
+//
+// The first and last handles go from 0 to 255 in a linear fashion.
+// The last and first handles are ordered so that the last handle
+// must be greater than or equal to the first handle.
+//
+// The middle handle's range goes from 9.99 to 0.01, counting down.
+// the middle handle's limits are set by the outer handles such that
+// the position of the left handle is the staring value (9.99) for the
+// middle handle. And the position of the right handle is the end
+// value (0.01). That means that the middle handle will move
+// proportionally as you move the outer handles.
+//
+// The two other interesting features of the middle handle are that
+// it counts down, and that it does so exponentially for the first
+// half of its range.
+//
+// Because the specification for the tag in HTML says that the
+// min should be less than the max, we do a double normalization to make
+// this work. The middle handle is considered to go between 0 and 1,
+// where 0 is the left handle's position and 1 is the right handle's
+// position. We then do the appropriate calculation to convert that
+// value into one between 9.99 and 0.01 for display to the user.
+//
+// One iteresting thing to note is that the normalization function
+// can also be used to enforce clamping.
+//
+export const ThreeHandlesComplex = (args: StoryArgs): TemplateResult => {
+ const values: { [key: string]: number } = {
+ black: 50,
+ gray: 4.98,
+ white: 225,
+ };
+ const handleEvent = (event: Event): void => {
+ const target = event.target as SliderHandle;
+ if (target.value != null) {
+ if (typeof target.value === 'object') {
+ action(event.type)(target.value);
+ } else {
+ action(event.type)(`${target.name}: ${target.value}`);
+ }
+ values[target.name] = target.value;
+ }
+ };
+ const grayNormalization = {
+ toNormalized(value: number) {
+ const normalizedBlack = values.black / 255;
+ const normalizedWhite = values.white / 255;
+ const clamped = Math.max(Math.min(value, 1), 0);
+ return (
+ clamped * (normalizedWhite - normalizedBlack) + normalizedBlack
+ );
+ },
+ fromNormalized(value: number) {
+ const normalizedBlack = values.black / 255;
+ const normalizedWhite = values.white / 255;
+ const clamped = Math.max(
+ Math.min(value, normalizedWhite),
+ normalizedBlack
+ );
+
+ return (
+ (clamped - normalizedBlack) /
+ (normalizedWhite - normalizedBlack)
+ );
+ },
+ };
+ const blackNormalization = {
+ toNormalized(value: number) {
+ const clamped = Math.min(value, values.white);
+ return clamped / 255;
+ },
+ fromNormalized(value: number) {
+ const denormalized = value * 255;
+ return Math.min(denormalized, values.white);
+ },
+ };
+ const whiteNormalization = {
+ toNormalized(value: number) {
+ const clamped = Math.max(value, values.black);
+ return clamped / 255;
+ },
+ fromNormalized(value: number) {
+ const denormalized = value * 255;
+ return Math.max(denormalized, values.black);
+ },
+ };
+ const computeGray = (value: number): string => {
+ let result = 1.0;
+ if (value > 0.5) {
+ result = Math.max(2 * (1 - value), 0.01);
+ } else if (value < 0.5) {
+ result = ((1 - 2 * value) * (Math.sqrt(9.99) - 1) + 1) ** 2;
+ }
+ const formatOptions = {
+ maximumFractionDigits: 2,
+ minimumFractionDigits: 2,
+ };
+ return new Intl.NumberFormat(navigator.language, formatOptions).format(
+ result
+ );
+ };
+ return html`
+