From b90ee5e811b7944905268b158031005a40c9b294 Mon Sep 17 00:00:00 2001 From: Tim Scharinger Date: Thu, 4 Jul 2024 11:07:13 +0200 Subject: [PATCH] feat(slider): add section labels support - add sections defined a range with edges - add section labels - add section labels padding-bottom - add section track colors - make slider slider label be active - add option so mark is only active when equal to slider value --- .vscode/settings.json | 8 +- .../__snapshots__/range-slider.spec.tsx.snap | 4 +- .../src/__snapshots__/slider.spec.tsx.snap | 4 +- .../slider/src/mark/label/mark-label.types.ts | 2 + .../slider/src/mark/label/use-mark-label.ts | 6 +- components/slider/src/mark/mark.types.ts | 2 + components/slider/src/range-slider.spec.tsx | 5 +- components/slider/src/render-slider.tsx | 27 ++-- .../slider/src/section/render-section.tsx | 10 ++ components/slider/src/section/section.tsx | 16 ++ .../slider/src/section/section.types.ts | 35 +++++ .../slider/src/section/use-section-styles.ts | 71 +++++++++ components/slider/src/section/use-section.ts | 43 ++++++ components/slider/src/slider.types.ts | 4 + components/slider/src/thumb/thumb.types.ts | 42 +++--- components/slider/src/use-range-slider.ts | 54 ++++++- components/slider/src/use-slider-styles.ts | 132 ++++++++++++++++- .../slider/examples/dual-section-example.tsx | 103 +++++++++++++ .../range-slider-with-section-example.tsx | 138 ++++++++++++++++++ .../examples/triple-section-example-rtl.tsx | 125 ++++++++++++++++ .../examples/triple-section-example.tsx | 125 ++++++++++++++++ .../triple-section-no-zero-start-example.tsx | 136 +++++++++++++++++ examples/src/stories/slider/slider-page.tsx | 50 ++++++- tools/generated/changelog/changeset.js | 2 +- tools/src/changelog/changeset.ts | 2 +- 25 files changed, 1092 insertions(+), 54 deletions(-) create mode 100644 components/slider/src/section/render-section.tsx create mode 100644 components/slider/src/section/section.tsx create mode 100644 components/slider/src/section/section.types.ts create mode 100644 components/slider/src/section/use-section-styles.ts create mode 100644 components/slider/src/section/use-section.ts create mode 100644 examples/src/stories/slider/examples/dual-section-example.tsx create mode 100644 examples/src/stories/slider/examples/range-slider-with-section-example.tsx create mode 100644 examples/src/stories/slider/examples/triple-section-example-rtl.tsx create mode 100644 examples/src/stories/slider/examples/triple-section-example.tsx create mode 100644 examples/src/stories/slider/examples/triple-section-no-zero-start-example.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 6e530784..03702aeb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,11 @@ "typescript.tsdk": "node_modules/typescript/lib", "eslint.workingDirectories": [ {"mode":"auto"} - ] + ], + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } } diff --git a/components/slider/src/__snapshots__/range-slider.spec.tsx.snap b/components/slider/src/__snapshots__/range-slider.spec.tsx.snap index 89c7a44e..10991f44 100644 --- a/components/slider/src/__snapshots__/range-slider.spec.tsx.snap +++ b/components/slider/src/__snapshots__/range-slider.spec.tsx.snap @@ -16,7 +16,7 @@ exports[`range-slider > should render 1`] = ` >
should render 1`] = ` >
should render 1`] = ` >
should render 1`] = ` >
& { value: number; label: ReactNode; + /** Label is ***active*** only when slider value is **equal** to mark value. */ + activeEqual?: boolean; }; export type MarkLabelState = diff --git a/components/slider/src/mark/label/use-mark-label.ts b/components/slider/src/mark/label/use-mark-label.ts index 40290db7..fe50ca8b 100644 --- a/components/slider/src/mark/label/use-mark-label.ts +++ b/components/slider/src/mark/label/use-mark-label.ts @@ -14,6 +14,10 @@ export const useMarkLabel_unstable = ( const minValue = values.length > 1 ? Math.min(...values) : 0; const maxValue = Math.max(...values); + const active = props.activeEqual + ? minValue === props.value || maxValue === props.value + : props.value >= minValue && props.value <= maxValue; + return { root: getNativeElementProps("span", { ref, @@ -26,6 +30,6 @@ export const useMarkLabel_unstable = ( offset: toPercent(props.value, min, max), value: props.value, disabled, - active: props.value >= minValue && props.value <= maxValue, + active, }; }; diff --git a/components/slider/src/mark/mark.types.ts b/components/slider/src/mark/mark.types.ts index b894c4af..4a949a48 100644 --- a/components/slider/src/mark/mark.types.ts +++ b/components/slider/src/mark/mark.types.ts @@ -8,6 +8,8 @@ import { export type MarkDef = { value: number; label?: ReactNode; + /** Mark label is ***active*** only when slider value is **equal** to mark value. */ + labelEqualActive?: boolean; }; export type MarkSlots = { diff --git a/components/slider/src/range-slider.spec.tsx b/components/slider/src/range-slider.spec.tsx index 32c13b3e..00d22ed2 100644 --- a/components/slider/src/range-slider.spec.tsx +++ b/components/slider/src/range-slider.spec.tsx @@ -1,9 +1,10 @@ +import React from "react"; +import { describe, expect, it, vi } from "vitest"; import { fireEvent, render } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; + import { RangeSlider } from "./range-slider"; -import React from "react"; import { getControlRoot } from "./test-helpers"; -import { expect, vi } from "vitest"; const expectSliderValues = (elements: HTMLElement[], values: number[]) => { expect(elements).toHaveLength(values.length); diff --git a/components/slider/src/render-slider.tsx b/components/slider/src/render-slider.tsx index 24baefcc..06247492 100644 --- a/components/slider/src/render-slider.tsx +++ b/components/slider/src/render-slider.tsx @@ -1,8 +1,9 @@ +import React from "react"; + import { getSlots } from "@fluentui/react-utilities"; import { SliderContextProvider } from "./context/slider-context"; import { SliderContextValues, SliderSlots, SliderState } from "./slider.types"; -import React from "react"; export const renderSlider_unstable = ( state: SliderState, @@ -10,7 +11,7 @@ export const renderSlider_unstable = ( ) => { const { slots, slotProps } = getSlots(state); - const { marks, markLabels, thumbs } = state; + const { marks, markLabels, sectionLabels, thumbs } = state; return ( @@ -18,14 +19,14 @@ export const renderSlider_unstable = ( - {marks.map((markProps) => ( - - ))} + {marks.map((markProps) => ( + + ))} {markLabels.map((markLabelProps) => ( ))} + + {sectionLabels.map((sectionLabelProps) => ( + + ))} {thumbs.map((thumbProps) => ( { + const { slots, slotProps } = getSlots(state); + + return ; +}; diff --git a/components/slider/src/section/section.tsx b/components/slider/src/section/section.tsx new file mode 100644 index 00000000..1ce564fc --- /dev/null +++ b/components/slider/src/section/section.tsx @@ -0,0 +1,16 @@ +import { ForwardRefComponent } from "@fluentui/react-utilities"; +import React from "react"; + +import { SectionProps } from "./section.types"; +import { useSection_unstable } from "./use-section"; +import { useSectionStyles_unstable } from "./use-section-styles"; +import { renderSection_unstable } from "./render-section"; + +export const Section: ForwardRefComponent = React + .forwardRef((props, ref) => { + const state = useSection_unstable(props, ref); + + useSectionStyles_unstable(state); + + return renderSection_unstable(state); + }); diff --git a/components/slider/src/section/section.types.ts b/components/slider/src/section/section.types.ts new file mode 100644 index 00000000..942b914f --- /dev/null +++ b/components/slider/src/section/section.types.ts @@ -0,0 +1,35 @@ +import { + ComponentProps, + ComponentState, + Slot, +} from "@fluentui/react-utilities"; +import { ReactNode } from "react"; + +export type SectionDef = { + edges: SectionEdges; + label: ReactNode; + trackColor?: string; +}; + +export type SectionSlots = { + root: Slot<"span">; +}; + +interface SectionEdges { + from?: number; + to?: number; +} + +export type SectionProps = ComponentProps & { + edges: Required; + label: ReactNode; + trackColor?: string; +}; + +export type SectionState = + & ComponentState + & { + offset: number; + disabled: boolean; + active: boolean; + }; diff --git a/components/slider/src/section/use-section-styles.ts b/components/slider/src/section/use-section-styles.ts new file mode 100644 index 00000000..41880000 --- /dev/null +++ b/components/slider/src/section/use-section-styles.ts @@ -0,0 +1,71 @@ +import { + makeStyles, + mergeClasses, + shorthands, + tokens, + useFluent, +} from "@fluentui/react-components"; + +import { + sliderClassNames, + sliderDurations, + sliderEasings, + sliderVars, +} from "../use-slider-styles"; +import { SectionState } from "./section.types"; + +const useStyles = makeStyles({ + root: { + ...shorthands.transition( + "color", + sliderDurations.short, + sliderEasings.easeOutFast + ), + position: "absolute", + color: `var(${sliderVars.section.color})`, + top: `var(${sliderVars.thumb.size})`, + transform: "translateX(-50%)", + whiteSpace: "nowrap", + }, + active: { + [sliderVars.section.color]: tokens.colorNeutralForeground1, + }, + enabled: { + [sliderVars.section.color]: tokens.colorNeutralForeground2, + }, + disabled: { + [sliderVars.section.color]: tokens.colorNeutralForegroundDisabled, + }, +}); + +export const useSectionStyles_unstable = ( + state: SectionState +): SectionState => { + const styles = useStyles(); + + const { offset, disabled, active } = state; + + const colorStyles = disabled + ? styles.disabled + : active + ? styles.active + : styles.enabled; + + state.root.className = mergeClasses( + sliderClassNames.section.label, + styles.root, + colorStyles, + state.root.className + ); + + const { dir } = useFluent(); + const offsetDirection = dir === "rtl" ? "right" : "left"; + state.root.title = ""; + state.root.style = { + cursor: "default", + [offsetDirection]: `${offset}%`, + ...state.root.style, + }; + + return state; +}; diff --git a/components/slider/src/section/use-section.ts b/components/slider/src/section/use-section.ts new file mode 100644 index 00000000..f385431a --- /dev/null +++ b/components/slider/src/section/use-section.ts @@ -0,0 +1,43 @@ +import { getNativeElementProps } from "@fluentui/react-utilities"; +import React from "react"; + +import { useSliderContext } from "../context/slider-context"; +import { SectionProps, SectionState } from "./section.types"; +import { toPercent } from "../utils"; + +export const useSection_unstable = ( + props: SectionProps, + ref: React.Ref +): SectionState => { + const { min, max, disabled, values } = useSliderContext(); + + const from = props.edges.from ?? min; + const to = props.edges.to ?? max; + + /** + * The sections center position, calculated as the midpoint between left and right. + */ + const center = from + (to - from) / 2; + + const minValue = values.length > 1 ? Math.min(...values) : min; + const maxValue = Math.max(...values); + + const active = + (minValue > from && minValue < to) || + (minValue <= from && maxValue > to) || + (maxValue > from && maxValue <= to); + + return { + root: getNativeElementProps("span", { + ref, + children: props.label, + ...props, + }), + components: { + root: "span", + }, + offset: toPercent(center, min, max), + disabled, + active, + }; +}; diff --git a/components/slider/src/slider.types.ts b/components/slider/src/slider.types.ts index ccc9e153..60ca2192 100644 --- a/components/slider/src/slider.types.ts +++ b/components/slider/src/slider.types.ts @@ -8,6 +8,7 @@ import { SliderContextValue } from "./context/slider-context"; import { MarkDef, MarkProps } from "./mark/mark.types"; import { ThumbProps } from "./thumb/thumb.types"; import { MarkLabelProps } from "./mark/label/mark-label.types"; +import { SectionDef, SectionProps } from "./section/section.types"; export type SliderContextValues = { slider: SliderContextValue; @@ -21,6 +22,7 @@ export type SliderSlots = { thumb: Slot>; mark: Slot>; markLabel: Slot>; + sectionLabel: Slot>; }; export type SliderOnChangeData = { @@ -39,6 +41,7 @@ export type RangeSliderProps = & { disabled?: boolean; marks?: boolean | MarkDef[]; + sectionLabels?: SectionDef[]; step?: number | "marks"; size?: "small" | "medium"; min: number; @@ -69,6 +72,7 @@ export type SliderState = values: number[]; marks: MarkProps[]; markLabels: MarkLabelProps[]; + sectionLabels: SectionProps[]; thumbs: ThumbProps[]; trackOffset: number; trackWidth: number; diff --git a/components/slider/src/thumb/thumb.types.ts b/components/slider/src/thumb/thumb.types.ts index cbf4fbc1..2fba859f 100644 --- a/components/slider/src/thumb/thumb.types.ts +++ b/components/slider/src/thumb/thumb.types.ts @@ -11,30 +11,26 @@ export type ThumbSlots = { label: NonNullable>; }; -export type ThumbProps = - & Omit< - ComponentProps, "input">, - "value" - > - & { - children?: never; - value: number; - handleFocus: () => void; - handleBlur: () => void; - handleMouseOver: () => void; - handleMouseLeave: () => void; - handleInputChange: (event: ChangeEvent) => void; - valueLabelTransform?: (value: number) => number | string; - active: boolean; - open: boolean; - dragging: boolean; - "data-index": number; - }; +export type ThumbProps = Omit< + ComponentProps, "input">, + "value" +> & { + children?: never; + value: number; + handleFocus: () => void; + handleBlur: () => void; + handleMouseOver: () => void; + handleMouseLeave: () => void; + handleInputChange: (event: ChangeEvent) => void; + valueLabelTransform?: (value: number) => number | string; + active: boolean; + open: boolean; + dragging: boolean; + "data-index": number; +}; -export type ThumbState = - & ComponentState - & Required> - & { +export type ThumbState = ComponentState & + Required> & { offset: number; disabled: boolean; }; diff --git a/components/slider/src/use-range-slider.ts b/components/slider/src/use-range-slider.ts index 2401370f..4b315d11 100644 --- a/components/slider/src/use-range-slider.ts +++ b/components/slider/src/use-range-slider.ts @@ -29,6 +29,8 @@ import { toPercent } from "./utils"; import { MarkLabelProps } from "./mark/label/mark-label.types"; import { MarkLabel } from "./mark/label/mark-label"; import { sliderClassNames } from "./use-slider-styles"; +import { SectionProps } from "./section/section.types"; +import { Section } from "./section/section"; const asc = (a: number, b: number): number => a - b; @@ -83,8 +85,9 @@ const findClosest = (value: number, candidates: number[]) => { const isMarkLabelElement = (target: EventTarget | null): boolean => { if ( - !target || !(target instanceof Element) - || target.classList.contains(sliderClassNames.root) + !target || + !(target instanceof Element) || + target.classList.contains(sliderClassNames.root) ) { return false; } @@ -94,6 +97,20 @@ const isMarkLabelElement = (target: EventTarget | null): boolean => { return isMarkLabelElement(target.parentNode); }; +const isSectionLabelElement = (target: EventTarget | null): boolean => { + if ( + !target || + !(target instanceof Element) || + target.classList.contains(sliderClassNames.root) + ) { + return false; + } + if (target.classList.contains(sliderClassNames.section.label)) { + return true; + } + return isSectionLabelElement(target.parentNode); +}; + export const useRangeSlider_unstable = ( props: RangeSliderProps, ref: React.Ref @@ -103,6 +120,7 @@ export const useRangeSlider_unstable = ( disabled, size, marks: markDefs, + sectionLabels: sectionDefs, step: stepProp, min, max, @@ -155,6 +173,7 @@ export const useRangeSlider_unstable = ( markLabels.push({ value: markDef.value, label: markDef.label, + activeEqual: markDef.labelEqualActive, }); } } @@ -162,6 +181,23 @@ export const useRangeSlider_unstable = ( return { marks, markLabels }; }, [markDefs, stepProp, steps]); + const sectionLabels: SectionProps[] = useMemo(() => { + const sectionLabels: SectionProps[] = []; + if (Array.isArray(sectionDefs)) { + for (const sectionDef of sectionDefs) { + sectionLabels.push({ + ...sectionDef, + edges: { + from: sectionDef.edges.from ?? min, + to: sectionDef.edges.to ?? max, + }, + }); + } + } + + return sectionLabels; + }, [sectionDefs, min, max]); + const markValues = useMemo(() => marks.map((m) => m.value), [marks]); const snapValues = useMemo(() => { @@ -234,6 +270,10 @@ export const useRangeSlider_unstable = ( // avoid text selection e.preventDefault(); + if (isSectionLabelElement(e.target)) { + return; + } + const closestThumbIndex = findClosest(newValue, internalValues).index; const newValues = internalValues.slice(); @@ -310,6 +350,10 @@ export const useRangeSlider_unstable = ( touchId.current = touch.identifier; } + if (isSectionLabelElement(event.target)) { + return; + } + const newValue = getNewValue( touch.clientX, isMarkLabelElement(event.target) @@ -351,7 +395,8 @@ export const useRangeSlider_unstable = ( }; }, [controlRef, handleTouchStart, removeListeners]); - const minValue = internalValues.length > 1 ? Math.min(...internalValues) : 0; + const minValue = + internalValues.length > 1 ? Math.min(...internalValues) : min; const maxValue = Math.max(...internalValues); const trackOffset = toPercent(minValue, min, max); @@ -441,6 +486,7 @@ export const useRangeSlider_unstable = ( }), mark: resolveShorthand(props.mark, { required: true }), markLabel: resolveShorthand(props.markLabel, { required: true }), + sectionLabel: resolveShorthand(props.sectionLabel, { required: true }), components: { root: "span", control: "span", @@ -449,9 +495,11 @@ export const useRangeSlider_unstable = ( thumb: Thumb as React.FC>, mark: Mark as React.FC>, markLabel: MarkLabel as React.FC>, + sectionLabel: Section as React.FC>, }, marks, markLabels, + sectionLabels, thumbs: internalValues.map((value, index) => ({ value, valueLabelTransform, diff --git a/components/slider/src/use-slider-styles.ts b/components/slider/src/use-slider-styles.ts index 49e4b712..cd658885 100644 --- a/components/slider/src/use-slider-styles.ts +++ b/components/slider/src/use-slider-styles.ts @@ -1,3 +1,5 @@ +import { useMemo } from "react"; + import { createFocusOutlineStyle, makeStyles, @@ -8,6 +10,7 @@ import { } from "@fluentui/react-components"; import { SliderState } from "./slider.types"; +import { toPercent } from "./utils"; export const sliderClassNames = { root: "axis-Slider", @@ -22,6 +25,10 @@ export const sliderClassNames = { root: "axis-Slider__mark", label: "axis-Slider__mark__label", }, + section: { + root: "axis-Slider__section", + label: "axis-Slider__section__label", + }, }; export const sliderVars = { @@ -45,6 +52,9 @@ export const sliderVars = { mark: { color: "--axis-Slider__mark--color", }, + section: { + color: "--axis-Slider__section--color", + }, }; export const sliderDurations = { @@ -92,6 +102,9 @@ const useRootStyles = makeStyles({ hasMarkLabel: { [sliderVars.root.paddingBottom]: "1rem", }, + hasSectionLabels: { + [sliderVars.root.paddingBottom]: "1rem", + }, }); const useControlStyles = makeStyles({ @@ -154,13 +167,23 @@ export const useSliderStyles_unstable = (state: SliderState): SliderState => { const railStyles = useRailStyles(); const trackStyles = useTrackStyles(); - const { disabled, trackOffset, trackWidth, active, markLabels, size } = state; + const { + disabled, + trackOffset, + trackWidth, + active, + markLabels, + sectionLabels, + size, + values, + } = state; const { dir } = useFluent(); const rtl = dir === "rtl"; const hasMarkLabel = markLabels.length > 0; + const hasSectionLabels = sectionLabels.length > 0; state.root.className = mergeClasses( sliderClassNames.root, @@ -169,6 +192,7 @@ export const useSliderStyles_unstable = (state: SliderState): SliderState => { size === "medium" && rootStyles.medium, rootStyles.focusIndicator, hasMarkLabel && rootStyles.hasMarkLabel, + hasSectionLabels && rootStyles.hasSectionLabels, state.root.className ); @@ -193,10 +217,112 @@ export const useSliderStyles_unstable = (state: SliderState): SliderState => { state.track.className ); + const hasSectionLabelsAndColors = sectionLabels.length > 0; + const offsetDirection = rtl ? "right" : "left"; + const offset = hasSectionLabelsAndColors + ? undefined + : { + [offsetDirection]: `${trackOffset}%`, + }; + + const sectionStyles = useMemo(() => { + /** trackColorGradient */ + let tcg; + let thumbColor; + + if (hasSectionLabelsAndColors) { + const { min, max } = state; + const [rangeStart, rangeEnd] = values; + const [trackStart, trackEnd] = + rangeEnd === undefined ? [min, rangeStart] : values; + + const defaultColor = tokens.colorCompoundBrandBackground; + + const gradientDirection = !rtl ? "right" : "left"; + + tcg = `linear-gradient(to ${gradientDirection}, `; + for (const sl of state.sectionLabels) { + const color = sl.trackColor ?? defaultColor; + + // start + if (sl.edges.from <= trackStart) { + if (sl.edges.from < trackStart) { + // transparent block before track + tcg += `transparent ${toPercent(sl.edges.from, min, max)}%, `; + tcg += `transparent ${toPercent(trackStart, min, max)}%, `; + } + + if (sl.edges.to > trackStart && sl.edges.to < trackEnd) { + tcg += `${color} ${toPercent(trackStart, min, max)}%, `; + tcg += `${color} ${toPercent(sl.edges.to, min, max)}%, `; + } + } + + // middle full + if ( + (sl.edges.from ?? max) >= trackStart && + (sl.edges.to ?? max) <= trackEnd + ) { + tcg += `${color} ${toPercent(sl.edges.from, min, max)}%, `; + tcg += `${color} ${toPercent(sl.edges.to, min, max)}%, `; + } + // middle inside + if ( + (sl.edges.from ?? max) <= trackStart && + (sl.edges.to ?? max) >= trackEnd + ) { + tcg += `${color} ${toPercent(trackStart, min, max)}%, `; + tcg += `${color} ${toPercent(trackEnd, min, max)}%, `; + + // transparent block after track + tcg += `transparent ${toPercent(trackEnd, min, max)}%, `; + tcg += `transparent ${toPercent(sl.edges.to, min, max)}%, `; + } + + // end + if (sl.edges.from > trackStart && sl.edges.to > trackEnd) { + // colored block of track + tcg += `${color} ${toPercent(sl.edges.from, min, max)}%, `; + tcg += `${color} ${toPercent(trackEnd, min, max)}%, `; + + // transparent block after track + tcg += `transparent ${toPercent(trackEnd, min, max)}%, `; + tcg += `transparent ${toPercent(sl.edges.to, min, max)}%, `; + } + + if (sl.edges.from > trackEnd) { + // transparent block after track + tcg += `transparent ${toPercent(sl.edges.from, min, max)}%, `; + tcg += `transparent ${toPercent(sl.edges.to, min, max)}%, `; + } + } + tcg = tcg.substring(0, tcg.length - 2); // remove trailing ", " + tcg += ")"; + + thumbColor = tokens.colorCompoundBrandBackground; + + for (const [index, sectionLabel] of state.sectionLabels.entries()) { + if ( + trackEnd === min ? index === 0 : trackEnd > sectionLabel.edges.from + ) { + thumbColor = sectionLabel.trackColor ?? defaultColor; + } + } + + return { trackColorGradient: tcg, thumbColor }; + } + }, [values]); + + state.thumb = { + style: { + backgroundColor: sectionStyles?.thumbColor, + }, + }; state.track.style = { - [offsetDirection]: `${trackOffset}%`, - width: `${trackWidth}%`, + backgroundImage: sectionStyles?.trackColorGradient, + width: hasSectionLabelsAndColors ? "100%" : `${trackWidth}%`, + ...offset, ...state.track.style, }; diff --git a/examples/src/stories/slider/examples/dual-section-example.tsx b/examples/src/stories/slider/examples/dual-section-example.tsx new file mode 100644 index 00000000..39a9ca86 --- /dev/null +++ b/examples/src/stories/slider/examples/dual-section-example.tsx @@ -0,0 +1,103 @@ +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +const markValue = 45.5; + +export function DualSectionSliderExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} + +export const DualSectionSliderExampleAsString = ` +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +const markValue = 45.5; + +export function DualSectionSliderExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} +`; diff --git a/examples/src/stories/slider/examples/range-slider-with-section-example.tsx b/examples/src/stories/slider/examples/range-slider-with-section-example.tsx new file mode 100644 index 00000000..d8281480 --- /dev/null +++ b/examples/src/stories/slider/examples/range-slider-with-section-example.tsx @@ -0,0 +1,138 @@ +import React from "react"; + +import { RangeSlider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function RangeSliderWithSectionExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} + +export const RangeSliderWithSectionExampleAsString = ` +import React from "react"; + +import { RangeSlider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function RangeSliderWithSectionExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +}`; diff --git a/examples/src/stories/slider/examples/triple-section-example-rtl.tsx b/examples/src/stories/slider/examples/triple-section-example-rtl.tsx new file mode 100644 index 00000000..668c1397 --- /dev/null +++ b/examples/src/stories/slider/examples/triple-section-example-rtl.tsx @@ -0,0 +1,125 @@ +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function TripleSectionSliderExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} + +export const TripleSectionSliderExampleAsString = ` +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function TripleSectionSliderExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} +`; diff --git a/examples/src/stories/slider/examples/triple-section-example.tsx b/examples/src/stories/slider/examples/triple-section-example.tsx new file mode 100644 index 00000000..834e7476 --- /dev/null +++ b/examples/src/stories/slider/examples/triple-section-example.tsx @@ -0,0 +1,125 @@ +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function TripleSectionSliderExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} + +export const TripleSectionSliderExampleAsString = ` +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function TripleSectionSliderExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} +`; diff --git a/examples/src/stories/slider/examples/triple-section-no-zero-start-example.tsx b/examples/src/stories/slider/examples/triple-section-no-zero-start-example.tsx new file mode 100644 index 00000000..4cb3113b --- /dev/null +++ b/examples/src/stories/slider/examples/triple-section-no-zero-start-example.tsx @@ -0,0 +1,136 @@ +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function TripleSectionSliderNoZeroStartExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +} + +export const TripleSectionSliderNoZeroStartExampleAsString = ` +import React from "react"; + +import { Slider } from "@axiscommunications/fluent-slider"; +import { makeStyles, tokens } from "@fluentui/react-components"; + +const useCustomizedSliderStyles = makeStyles({ + mark: { + height: "16px", + width: "1px", + backgroundColor: "white", + }, + markLabel: { + color: tokens.colorPaletteRedForeground1, + }, + track: { + backgroundColor: "gray", + }, +}); + +export function TripleSectionSliderNoZeroStartExample() { + const classes = useCustomizedSliderStyles(); + return ( + + ); +}`; diff --git a/examples/src/stories/slider/slider-page.tsx b/examples/src/stories/slider/slider-page.tsx index 01d9d2cd..b165a0f2 100644 --- a/examples/src/stories/slider/slider-page.tsx +++ b/examples/src/stories/slider/slider-page.tsx @@ -56,6 +56,23 @@ import { WithStepsAndMarksRangeSliderExampleAsString, } from "./examples/range-slider-with-streps-and-marks-example"; +import { + DualSectionSliderExample, + DualSectionSliderExampleAsString, +} from "./examples/dual-section-example"; +import { + TripleSectionSliderExample, + TripleSectionSliderExampleAsString, +} from "./examples/triple-section-example"; +import { + TripleSectionSliderNoZeroStartExample, + TripleSectionSliderNoZeroStartExampleAsString, +} from "./examples/triple-section-no-zero-start-example"; +import { + RangeSliderWithSectionExample, + RangeSliderWithSectionExampleAsString, +} from "./examples/range-slider-with-section-example"; + const useStyles = makeStyles({ example: { maxWidth: "auto", @@ -135,6 +152,31 @@ const examples: pageData[] = [ example: , codeString: WithStepsRangeSliderExampleAsString, }, + + { + title: "Dual section", + anchor: "DualSectionSliderExample", + example: , + codeString: DualSectionSliderExampleAsString, + }, + { + title: "Triple section", + anchor: "TripleSectionSliderExample", + example: , + codeString: TripleSectionSliderExampleAsString, + }, + { + title: "Triple section with negative section", + anchor: "TripleSectionSliderNoZeroStartExample", + example: , + codeString: TripleSectionSliderNoZeroStartExampleAsString, + }, + { + title: "Range slider with section", + anchor: "RangeSliderWithSliderExample", + example: , + codeString: RangeSliderWithSectionExampleAsString, + }, ]; export const SliderPage = () => { @@ -142,14 +184,10 @@ export const SliderPage = () => { const styles = useStyles(); const { renderSections, renderNavigation } = useExampleWithNavigation( - examples.map(d => { + examples.map((d) => { return { ...d, - example: ( -
- {d.example} -
- ), + example:
{d.example}
, }; }) ); diff --git a/tools/generated/changelog/changeset.js b/tools/generated/changelog/changeset.js index 0eca7d17..e31f1850 100644 --- a/tools/generated/changelog/changeset.js +++ b/tools/generated/changelog/changeset.js @@ -63,7 +63,7 @@ function parseConventionalCommitMessage(msg) { if (match === null) { throw new Error("no matches found"); } - const [_, group, scope, breaking, title] = match; + const [, group, scope, breaking, title] = match; return { group, scope, diff --git a/tools/src/changelog/changeset.ts b/tools/src/changelog/changeset.ts index 0df373c7..b4f6ef33 100644 --- a/tools/src/changelog/changeset.ts +++ b/tools/src/changelog/changeset.ts @@ -115,7 +115,7 @@ function parseConventionalCommitMessage(msg: string): ConventionalCommit { if (match === null) { throw new Error("no matches found"); } - const [_, group, scope, breaking, title] = match; + const [, group, scope, breaking, title] = match; return { group, scope,