UNPKG

@workday/canvas-kit-react

Version:

The parent module that contains all Workday Canvas Kit React components

245 lines (244 loc) • 12 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InputGroup = exports.inputGroupStencil = exports.useClearButton = exports.inputGroupInputStencil = exports.useInputGroupInput = exports.inputGroupInnerStencil = exports.useInputGroupModel = void 0; const react_1 = __importDefault(require("react")); const canvas_kit_styling_1 = require("@workday/canvas-kit-styling"); const canvas_tokens_web_1 = require("@workday/canvas-tokens-web"); const common_1 = require("@workday/canvas-kit-react/common"); const layout_1 = require("@workday/canvas-kit-react/layout"); const button_1 = require("@workday/canvas-kit-react/button"); const canvas_system_icons_web_1 = require("@workday/canvas-system-icons-web"); const TextInput_1 = require("./TextInput"); exports.useInputGroupModel = (0, common_1.createModelHook)({})(() => { const inputRef = react_1.default.useRef(null); return { state: { inputRef, }, events: {}, }; }); exports.inputGroupInnerStencil = (0, canvas_kit_styling_1.createStencil)({ vars: { /** * Offset of the inner item. Set by the `InputGroup` and depends on siblings. Do not change this * on your own. */ insetInlineStart: 'initial', /** * Offset of the inner item. Set by the `InputGroup` and depends on siblings. Do not change this * on your own. */ insetInlineEnd: 'initial', width: canvas_tokens_web_1.system.space.x10, height: canvas_tokens_web_1.system.space.x10, /** * Some inner input group elements are decoration only and should not have pointer events */ pointerEvents: '', }, base: { name: "d63q44", styles: "--insetInlineStart-input-group-inner-42c96b:initial;--insetInlineEnd-input-group-inner-42c96b:initial;--width-input-group-inner-42c96b:var(--cnvs-sys-space-x10);--height-input-group-inner-42c96b:var(--cnvs-sys-space-x10);box-sizing:border-box;display:flex;position:absolute;align-items:center;justify-content:center;width:var(--width-input-group-inner-42c96b);height:var(--height-input-group-inner-42c96b);inset-inline-start:var(--insetInlineStart-input-group-inner-42c96b);inset-inline-end:var(--insetInlineEnd-input-group-inner-42c96b);" }, modifiers: { pointerEvents: { _: { name: "d63q45", styles: "pointer-events:var(--pointerEvents-input-group-inner-42c96b);" } } } }, "input-group-inner-42c96b"); const InputGroupInnerStart = (0, common_1.createSubcomponent)('div')({ modelHook: exports.useInputGroupModel, })(({ pointerEvents, insetInlineStart, insetInlineEnd, width, height, ...elemProps }, Element) => { return (react_1.default.createElement(Element, { ...(0, layout_1.mergeStyles)(elemProps, (0, exports.inputGroupInnerStencil)({ pointerEvents, insetInlineStart: toPx(insetInlineStart), insetInlineEnd: toPx(insetInlineEnd), width: toPx(width), height: toPx(height), })) })); }); const InputGroupInnerEnd = (0, common_1.createSubcomponent)('div')({ modelHook: exports.useInputGroupModel, })(({ pointerEvents, insetInlineStart, insetInlineEnd, width, height, ...elemProps }, Element) => { return (react_1.default.createElement(Element, { ...(0, layout_1.mergeStyles)(elemProps, (0, exports.inputGroupInnerStencil)({ pointerEvents, insetInlineStart: insetInlineStart, insetInlineEnd: insetInlineEnd, width: toPx(width), height: toPx(height), })) })); }); exports.useInputGroupInput = (0, common_1.createElemPropsHook)(exports.useInputGroupModel)((model, ref) => { const elementRef = (0, common_1.useForkRef)(ref, model.state.inputRef); return { ref: elementRef, placeholder: '', // Make sure a placeholder attribute always exists for `:placeholder-shown` }; }); exports.inputGroupInputStencil = (0, canvas_kit_styling_1.createStencil)({ vars: { paddingInlineStart: '', paddingInlineEnd: '', }, base: { name: "d63q46", styles: "box-sizing:border-box;display:flex;width:100%;" }, modifiers: { paddingInlineStart: { _: { name: "d63q47", styles: "padding-inline-start:var(--paddingInlineStart-input-group-input-9155da);" } }, paddingInlineEnd: { _: { name: "d63q48", styles: "padding-inline-end:var(--paddingInlineEnd-input-group-input-9155da);" } } } }, "input-group-input-9155da"); const InputGroupInput = (0, common_1.createSubcomponent)(TextInput_1.TextInput)({ modelHook: exports.useInputGroupModel, elemPropsHook: exports.useInputGroupInput, })(({ paddingInlineStart, paddingInlineEnd, ...elemProps }, Element) => { return (react_1.default.createElement(Element, { as: Element, ...(0, layout_1.mergeStyles)(elemProps, (0, exports.inputGroupInputStencil)({ paddingInlineStart: toPx(paddingInlineStart), paddingInlineEnd: toPx(paddingInlineEnd), })) })); }); exports.useClearButton = (0, common_1.createElemPropsHook)(exports.useInputGroupModel)(model => { return { // This element does not need to be accessible via screen reader. The user can already clear // an input role: 'presentation', // A clear input button doesn't need focus. There's already keyboard keys to clear an input tabIndex: -1, icon: canvas_system_icons_web_1.xSmallIcon, // "small" is needed to render correctly within a `TextInput` size: 'small', // prevent a focus change to the button. Focus should stay in the input. onMouseDown(event) { event.preventDefault(); }, onClick() { // This will clear the input's value (0, common_1.dispatchInputEvent)(model.state.inputRef.current, ''); }, }; }); /** * A clear input button. This can be a component later. */ const ClearButton = (0, common_1.createSubcomponent)(button_1.TertiaryButton)({ modelHook: exports.useInputGroupModel, elemPropsHook: exports.useClearButton, })((elemProps, Element) => { return react_1.default.createElement(Element, { "data-part": "input-group-clear-button", ...(0, canvas_kit_styling_1.handleCsProp)(elemProps) }); }); // make sure we always use pixels if the input is a number - this is required for `calc` const toPx = (input) => { return typeof input === 'number' ? `${input}px` : input; }; // wrap an array of widths into something the browser can understand, including `calc` for multiple // values const wrapInCalc = (values) => { if (values.length === 0) { return undefined; } if (values.length === 1) { return values[0]; } return `calc(${values.map(toPx).join(' + ')})`; }; exports.inputGroupStencil = (0, canvas_kit_styling_1.createStencil)({ base: { name: "d63q49", styles: "box-sizing:border-box;display:flex;position:relative;& :has([data-part=\"input-group-clear-button\"]){transition:opacity 300ms ease;}&:where(:has(input:placeholder-shown)) :has([data-part=\"input-group-clear-button\"]){opacity:0;pointer-events:none;}" } }, "input-group-27e30b"); /** * An `InputGroup` is a container around a {@link TextInput} with optional inner start and end * elements. The inner start and end elements are usually icons or icon buttons visually represented * inside the input. The `InputGroup` will add padding to the input so the icons/buttons display * correctly. This component uses `React.Children.map` and `React.cloneElement` from the * [React.Children](https://react.dev/reference/react/Children) API. This means all children must be * `InputGroup.*` components. Any other direct children will cause issues. You can add different * elements/components inside the {@link InputGroupInnerStart InputGroup.InnerStart} and * {@link InputGroupInnerEnd InputGroup.InnerEnd} subcomponents. * * ```tsx * <InputGroup> * <InputGroup.InnerStart as={SystemIcon} pointerEvents="none" icon={searchIcon} /> * <InputGroup.Input /> * <InputGroup.InnerEnd> * <TertiaryButton tabIndex={-1} icon={xIcon} size="small" /> * </InputGroup.InnerEnd> * </InputGroup> * ``` */ exports.InputGroup = (0, common_1.createContainer)('div')({ displayName: 'InputGroup', modelHook: exports.useInputGroupModel, subComponents: { /** * A component to show inside and at the start of the input. The input's padding will be * adjusted by the `InputGroup` to not overlap with this element. Use `width` to adjust the * width offset. The width defaults to 40px which is the correct width for icons or icon * buttons. */ InnerStart: InputGroupInnerStart, /** * The input to render. By default, this is a {@link TextInput}. Use the `as` prop to change the * component to be rendered. */ Input: InputGroupInput, /** * A component to show inside and at the end of the input. The input's padding will be adjusted * by the `InputGroup` to not overlap with this element. Use `width` to adjust the width offset. * The width defaults to 40px which is the correct width for icons or icon buttons within the * input. */ InnerEnd: InputGroupInnerEnd, /** * A component that can be added to an input group that will clear the input. It will only render * when the input has a value and will fade when a value is entered. */ ClearButton: ClearButton, }, })(({ children, ...elemProps }, Element) => { const offsetsStart = []; const offsetsEnd = []; // Collect the widths of the `InnerStart` and `InnerEnd` components into `offsetStart` and // `offsetEnd` arrays react_1.default.Children.forEach(children, child => { if (react_1.default.isValidElement(child) && child.type === InputGroupInnerStart) { const width = (0, canvas_kit_styling_1.wrapProperty)(child.props.width || canvas_tokens_web_1.system.space.x10); offsetsStart.push(width); } if (react_1.default.isValidElement(child) && child.type === InputGroupInnerEnd) { const width = (0, canvas_kit_styling_1.wrapProperty)(child.props.width || canvas_tokens_web_1.system.space.x10); offsetsEnd.push(width); } }); // keep track of the index offsets to make sure we calculate the correct position offset let indexStart = 0; let indexEnd = 0; // Loop over all the children and set the correct padding and positions const mappedChildren = react_1.default.Children.map(children, child => { if (react_1.default.isValidElement(child)) { if (child.type === InputGroupInput) { return react_1.default.cloneElement(child, { paddingInlineStart: wrapInCalc(offsetsStart), paddingInlineEnd: wrapInCalc(offsetsEnd), }); } if (child.type === InputGroupInnerStart) { const offset = wrapInCalc(offsetsStart.slice(0, indexStart)) || '0px'; indexStart++; return react_1.default.cloneElement(child, { insetInlineStart: offset, }); } if (child.type === InputGroupInnerEnd) { const offset = wrapInCalc(offsetsEnd.slice(indexEnd + 1, offsetsEnd.length)) || '0px'; indexEnd++; return react_1.default.cloneElement(child, { insetInlineEnd: offset, }); } } return child; }); return react_1.default.createElement(Element, { ...(0, layout_1.mergeStyles)(elemProps, (0, exports.inputGroupStencil)()) }, mappedChildren); });