diff --git a/public/content/documentation/web/component/range-slider.md b/public/content/documentation/web/component/range-slider.md index 86d7edeb..bf711eb5 100644 --- a/public/content/documentation/web/component/range-slider.md +++ b/public/content/documentation/web/component/range-slider.md @@ -94,13 +94,8 @@ While there is a native HTML range input, it is **difficult to style reliably** ```html
- - + +
+
``` - + diff --git a/src/components/content-display/markdown-content/markdown-content.tsx b/src/components/content-display/markdown-content/markdown-content.tsx index bfc04939..0dc0a529 100644 --- a/src/components/content-display/markdown-content/markdown-content.tsx +++ b/src/components/content-display/markdown-content/markdown-content.tsx @@ -56,6 +56,63 @@ const MarkdownContent: React.FC = ({ : `${assetBasePath}/${src}`; return ; }, + input: (props) => { + const type = (props)?.type; + + if (type !== 'range') { + return ; + } + + const fnKey = (props as any)['data-fn']; + const eventType = (props as any)['data-event'] || 'onInput'; + const fn = fnKey && markdownFunctionMap[fnKey]; + const min = (props).min ?? 0; + const max = (props).max ?? 100; + const step = (props).step ?? 1; + + const userOnInput = + typeof fn === 'function' && (eventType === 'onInput' || !eventType) + ? (event: React.FormEvent) => fn(event) + : undefined; + const userOnChange = + typeof fn === 'function' && eventType === 'onChange' + ? (event: React.ChangeEvent) => fn(event) + : undefined; + + const syncInputValue = (event: React.SyntheticEvent) => { + const target = event.currentTarget as HTMLInputElement | null; + if (!target) return; + const group = (target as HTMLElement).closest('.range-group'); + if (!group) return; + const valueEl = group.querySelector('.range-value') as HTMLElement | null; + if (!valueEl) return; + + valueEl.textContent = target.value; + valueEl.setAttribute('data-value', target.value); + }; + + const onInput = (e: React.FormEvent) => { + syncInputValue(e); + if (userOnInput) userOnInput(e); + }; + + const onChange = (e: React.ChangeEvent) => { + syncInputValue(e); + if (userOnChange) userOnChange(e); + }; + + return ( + + ); + }, a: (props) => { const fnKey = (props as any)['data-fn']; const eventType = (props as any)['data-event'] || 'onClick'; diff --git a/src/shared/content.json b/src/shared/content.json index 90eafeac..c2a96d3c 100644 --- a/src/shared/content.json +++ b/src/shared/content.json @@ -340,7 +340,7 @@ "videos": null, "androidDeveloperNotes": null, "iosDeveloperNotes": null, - "developerNotes": "## Code examples\n\nThis is one of the exceedingly rare instances where a custom element makes a lot of sense.\n\n### Use a custom element\n\n* Custom elements are **easier to style reliably** across browsers.\n* [Working slider pattern examples](https://www.w3.org/WAI/ARIA/apg/patterns/slider/)\n\n```html\n
\n How much cowbell?\n
\n
\n
\n
\n
\n```\n\n### Semantic HTML\n\nWhile there is a native HTML range input, it is **difficult to style reliably** across browsers.\n\n```html\n
\n \n \n
\n \n \n
\n
\n```\n\n" + "developerNotes": "## Code examples\n\nThis is one of the exceedingly rare instances where a custom element makes a lot of sense.\n\n### Use a custom element\n\n* Custom elements are **easier to style reliably** across browsers.\n* [Working slider pattern examples](https://www.w3.org/WAI/ARIA/apg/patterns/slider/)\n\n```html\n
\n How much cowbell?\n
\n
\n
\n
\n
\n```\n\n### Semantic HTML\n\nWhile there is a native HTML range input, it is **difficult to style reliably** across browsers.\n\n```html\n
\n \n 10\n
\n \n \n
\n \n
\n```\n\n\n
\n 10\n
\n \n \n
\n
\n
" }, { "label": "Scrolling Container", diff --git a/src/utils/markdownFunctions.ts b/src/utils/markdownFunctions.ts index 4b5e6bc4..5b981079 100644 --- a/src/utils/markdownFunctions.ts +++ b/src/utils/markdownFunctions.ts @@ -1,5 +1,5 @@ import { NavigateFunction } from 'react-router-dom'; - +import React from 'react'; /** * A map of callable functions that can be referenced inside Markdown files rendered as HTML. * @@ -21,7 +21,7 @@ import { NavigateFunction } from 'react-router-dom'; */ export const getMarkdownFunctionMap = ( navigate: NavigateFunction -): Record) => void> => ({ +): Record void> => ({ showAlert: () => alert('This works with a keyboard and a mouse!'), showAlertWhenDisabled: () => alert('This disabled button is still actionable for mouse and screen readers users!'), @@ -156,16 +156,16 @@ export const getMarkdownFunctionMap = ( if (!stepperSelect || stepperSelect.tagName.toLowerCase() !== 'select') { return; // Exit if the provided element is not a select element } - + let currentIndex = stepperSelect.selectedIndex; let nextIndex = currentIndex + 1; - + if (nextIndex < stepperSelect.options.length) { stepperSelect.selectedIndex = nextIndex; if (liveRegion) { liveRegion.innerHTML = `Quantity updated, ${nextIndex+1}`; - + setTimeout(() => { liveRegion.innerHTML = ''; }, 2000); @@ -180,10 +180,10 @@ export const getMarkdownFunctionMap = ( if (!stepperSelect || stepperSelect.tagName.toLowerCase() !== 'select') { return; // Exit if the provided element is not a select element } - + let currentIndex = stepperSelect.selectedIndex; let nextIndex = currentIndex - 1; - + if (currentIndex===0) { return; // do nothing if at 0 } else if (nextIndex < stepperSelect.options.length) { @@ -191,7 +191,7 @@ export const getMarkdownFunctionMap = ( if (liveRegion) { liveRegion.innerHTML = `Quantity updated, ${currentIndex}`; - + setTimeout(() => { liveRegion.innerHTML = ''; }, 2000); @@ -221,11 +221,11 @@ export const getMarkdownFunctionMap = ( // Get current state const isExpanded = toggleButton.getAttribute('aria-expanded') === 'true'; - - // Toggle the state - use only aria-expanded attribute + + // Toggle the state - use only aria-expanded attribute const newExpandedState = !isExpanded; toggleButton.setAttribute('aria-expanded', String(newExpandedState)); - + },