@atlaskit/toggle
Version:
A toggle is used to view or switch between enabled or disabled states.
177 lines (176 loc) • 6.95 kB
JavaScript
/* toggle.tsx generated by @compiled/babel-plugin v0.36.1 */
import _extends from "@babel/runtime/helpers/extends";
import "./toggle.compiled.css";
import { ax, ix } from "@compiled/react/runtime";
import React, { forwardRef, memo, useEffect, useRef, useState } from 'react';
import { bindAll } from 'bind-event-listener';
import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next';
import __noop from '@atlaskit/ds-lib/noop';
import { useId } from '@atlaskit/ds-lib/use-id';
import CheckMarkIcon from '@atlaskit/icon/utility/migration/check-mark--editor-done';
import CloseIcon from '@atlaskit/icon/utility/migration/cross--editor-close';
import { fg } from '@atlaskit/platform-feature-flags';
import { B200, G400, G500, N0, N20, N200, N400, N70 } from '@atlaskit/theme/colors';
import IconContainer from './icon-container';
const basicStyles = null;
const borderStyles = null;
const iconStyles = null;
const sizeStyles = {
regular: "_2rkopxbi _4t3ipxbi _1bsbxy5q _kfgtutpp _1mp41crf _p9c41fbe",
large: "_2rkov47k _4t3iv47k _1bsb1jfw _kfgtpxbi _1mp47vkz _p9c4w12q"
};
const noop = __noop;
const analyticsAttributes = {
componentName: 'toggle',
packageName: "@atlaskit/toggle",
packageVersion: "15.0.4"
};
const iconSizeMap = {
regular: 'small',
large: 'medium'
};
/**
* __Toggle__
*
* A toggle is used to view or switch between enabled or disabled states.
*
* - [Examples](https://atlassian.design/components/toggle/examples)
* - [Code](https://atlassian.design/components/toggle/code)
* - [Usage](https://atlassian.design/components/toggle/usage)
*/
const Toggle = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
const {
defaultChecked = false,
isDisabled = false,
onBlur: providedOnBlur = noop,
onChange: providedChange = noop,
onFocus: providedFocus = noop,
size = 'regular',
name = '',
value = '',
isChecked,
analyticsContext,
id,
testId,
label,
descriptionId
} = props;
const isControlled = typeof isChecked === 'undefined';
const [checked, setChecked] = useState(defaultChecked);
const [isKeyboardUsed, setIsKeyboardUsed] = useState(true);
const wrapperRef = useRef(null);
useEffect(() => {
if (id && wrapperRef.current && wrapperRef.current.parentElement) {
/*
DSP-21524 Handling the click on <label> that is linked via "for" attribute.
By default click on the label fires absolutely same onclick event as click on the input element.
To differentiate keyboard click from mouse we need this additional listener.
*/
const linkedLabel = wrapperRef.current.parentElement.querySelector(`label[for="${id}"]`);
if (linkedLabel) {
const unbind = bindAll(linkedLabel, [{
type: 'click',
listener: event => {
setIsKeyboardUsed(false);
if ((event === null || event === void 0 ? void 0 : event.detail) > 1) {
/*
DSP-21524 double or triple click on label initiating the text selection for label text and adds additional step to tab order.
So here we set the isKeyboardUsed to true, to display focus ring on next Tab press
*/
setIsKeyboardUsed(true);
}
}
}]);
return unbind;
}
}
}, [id, wrapperRef, isKeyboardUsed]);
const handleBlur = usePlatformLeafEventHandler({
fn: (event, analyticsEvent) => {
if (!isKeyboardUsed) {
setIsKeyboardUsed(true);
}
providedOnBlur(event, analyticsEvent);
},
action: 'blur',
analyticsData: analyticsContext,
...analyticsAttributes
});
const handleFocus = usePlatformLeafEventHandler({
fn: providedFocus,
action: 'focus',
analyticsData: analyticsContext,
...analyticsAttributes
});
const handleChange = usePlatformLeafEventHandler({
fn: (event, analyticsEvent) => {
if (isControlled) {
setChecked(checked => !checked);
}
return providedChange(event, analyticsEvent);
},
action: 'change',
analyticsData: analyticsContext,
...analyticsAttributes
});
const onLabelKeyDown = event => {
if ([' ', 'Tab', 'Space'].includes(event.key)) {
setIsKeyboardUsed(true);
}
};
const shouldChecked = isControlled ? checked : isChecked;
const controlProps = {
'data-checked': shouldChecked ? shouldChecked : undefined,
'data-disabled': isDisabled ? isDisabled : undefined,
'data-size': size,
'data-testid': testId ? testId : undefined,
// DSP-21524 Because label gets focus ring via focus-within and focus-within also triggers by mouse click we have to manually control the ring appearance.
onKeyDown: onLabelKeyDown,
onMouseDown: () => {
setIsKeyboardUsed(false);
}
};
const legacyIconSize = iconSizeMap[size];
const labelId = useId();
return /*#__PURE__*/React.createElement("label", _extends({}, controlProps, {
ref: wrapperRef,
className: ax(["_v564kete _1h6d1j28 _1dqonqa1 _189eyh40 _1e0c1o8l _vchh1ntv _kqswh2mm _1y1m1ntv _bfhk76s1 _syaz5w2r _6rthv77o _1pfhv77o _12l2v77o _ahbqv77o _85i5v77o _1q51v77o _y4tiv77o _bozgv77o _1i2uidpf _1i3tidpf _se28idpf _3tunidpf _1423idpf _1hrtidpf _1bz2idpf _rqfvidpf _1yc0glyw _onsr1snc _1vgmagmp _1av610yn _1ulhidpf _rfx31q5u _pdykkete _1cs8stnw _1rus5w2r _1kt9b3bt _1fkr1y44 _z5wt1y44 _7dwzglyw _1bki1y1w _1flt1y1w _3xv21y1w _9471g4a2 _ucab5w2r _mqf815t7 _3dj215t7 _1dl915t7 _1div5w2r _1il8kb7n _y54d5w2r _irr353wy _d0altlke _19ja13gf _8muf13gf _1k1q13gf _1plud4ly _1uo853wy _178c1y1w _16rslghj _ogf41r31 _1rtbnqa1 _5gg9t94y", isKeyboardUsed && "_jyzf194a _1r4mnqa1 _1t9pyh40", size === 'large' && !fg('platform-visual-refresh-icons') && "_1n82gktf _19vpgktf", sizeStyles[size]])
}), label ? /*#__PURE__*/React.createElement("span", {
id: labelId,
hidden: true
}, label) : null, /*#__PURE__*/React.createElement("input", {
ref: ref,
checked: shouldChecked,
disabled: isDisabled,
id: id,
name: name,
onBlur: handleBlur,
onChange: handleChange,
onFocus: handleFocus,
type: "checkbox",
value: value,
"data-testid": testId && `${testId}--input`,
"aria-labelledby": label ? labelId : undefined,
"aria-describedby": descriptionId
}), /*#__PURE__*/React.createElement(IconContainer, {
size: size,
isHidden: !shouldChecked,
position: "left"
}, /*#__PURE__*/React.createElement(CheckMarkIcon, {
label: "",
color: "currentColor",
LEGACY_size: legacyIconSize,
testId: testId && `${testId}--toggle-check-icon`
})), /*#__PURE__*/React.createElement(IconContainer, {
size: size,
isHidden: shouldChecked,
position: "right"
}, /*#__PURE__*/React.createElement(CloseIcon, {
label: "",
color: "currentColor",
LEGACY_size: legacyIconSize,
testId: testId && `${testId}--toggle-cross-icon`
})));
}));
Toggle.displayName = 'Toggle';
export default Toggle;