UNPKG

@workday/canvas-kit-react

Version:

The parent module that contains all Workday Canvas Kit React components

194 lines (193 loc) • 8.73 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useBaseListModel = exports.defaultGetTextValue = exports.defaultGetId = void 0; const react_1 = __importDefault(require("react")); const react_virtual_1 = require("./react-virtual"); const common_1 = require("@workday/canvas-kit-react/common"); const defaultGetId = (item) => { if (process.env.NODE_ENV === 'development') { if (typeof item === 'object' && item.id === undefined) { console.warn("List item was an object, but no `getId` was passed to the model to inform the list where to find the item's identifier. Please pass a `getId` to the list model"); } } return item === undefined ? '' : typeof item === 'string' ? item : item.id || ''; }; exports.defaultGetId = defaultGetId; const defaultGetTextValue = (item) => { if (process.env.NODE_ENV === 'development') { if (typeof item === 'object' && item.text === undefined) { console.warn("List item was an object, but no `getTextValue` was passed to the model to inform the list where to find the item's text value. The item's text value is used for accessibility. Please pass a `getTextValue` to the list model"); } } return typeof item === 'string' ? item : item === undefined ? '' : item.text || ''; }; exports.defaultGetTextValue = defaultGetTextValue; // interface Collection<T> { // getKeys(): string[]; // getItem(id: string): Item<T> | null; // at(index: number): Item<T> | null; // size: number; // } // TODO: Build an interface that allows representation of different collections. For example an array and a dynamically loaded dataset // class StaticCollection<T> implements Collection<T> { // // eslint-disable-next-line no-empty-function // private items: Item<T>[]; // constructor(items: T[], getId = defaultGetId, getTextValue = defaultGetTextValue) { // this.items = items.map((item, index) => ({ // index, // value: item, // id: getId(item), // textValue: getTextValue(item), // })); // } // getKeys() { // return this.items.map(item => item.id); // } // getItem(id: string) { // return this.items.find(item => item.id === id) || null; // } // at(index: number) { // return this.items[index] || null; // } // get size() { // return this.items.length; // } // } // force Typescript to use `Generic` as a symbol const genericDefaultConfig = { items: [], }; exports.useBaseListModel = (0, common_1.createModelHook)({ defaultConfig: { ...genericDefaultConfig, /** IDREF of the list. Children ids can be derived from this id */ id: '', /** * Optional function to return an id of an item. If not provided, the default function will * return the `id` property from the object of each item. If you did not provide `items`, do not * override this function. If you don't provided `items` and instead provide static items via * JSX, the list will create an internal array of items where `id` is the only property and the * default `getId` will return the desired result. */ getId: exports.defaultGetId, /** * Optional function to return the text representation of an item. If not provided, the default * function will return the `text` property of the object of each item or an empty string if * there is no `text` property. If you did not provide `items`, do not override this function. */ getTextValue: exports.defaultGetTextValue, /** * Array of all ids which are currently disabled. This is used for navigation to skip over items * which are not focusable. */ nonInteractiveIds: [], /** * The orientation of a list of items. Values are either `vertical` or `horizontal`. This value will * effect which ids activate progression through a list. For example, `horizontal` will activate with * left and right arrows while `vertical` will activate with up and down arrows. * @default 'vertical' */ orientation: 'vertical', /** * Best guess to the default item height for virtualization. Getting this number correct * avoids a rerender while the list is initializing. * * @default 50 */ defaultItemHeight: 50, shouldVirtualize: true, }, })(config => { var _a; const id = (0, common_1.useUniqueId)(config.id); // Optimization to not redo items when `getId` and `getTextValue` references change. They will not // likely change during the lifecycle and we don't want to recalculate items when a lamba is // passed instead of a stable reference. const getIdRef = react_1.default.useRef(exports.defaultGetId); const getTextValueRef = react_1.default.useRef(exports.defaultGetTextValue); getIdRef.current = config.getId || exports.defaultGetId; getTextValueRef.current = config.getTextValue || config.getId || exports.defaultGetTextValue; const [orientation] = react_1.default.useState(config.orientation || 'vertical'); const [UNSTABLE_defaultItemHeight, setDefaultItemHeight] = react_1.default.useState(config.defaultItemHeight); const isVirtualized = config.shouldVirtualize && !!((_a = config.items) === null || _a === void 0 ? void 0 : _a.length); const indexRef = react_1.default.useRef(0); const containerRef = react_1.default.useRef(null); const items = react_1.default.useMemo(() => (config.items || []).map((item, index) => { return { id: getIdRef.current(item), index, value: item, textValue: getTextValueRef.current(item), }; }), [config.items]); const [staticItems, setStaticItems] = react_1.default.useState([]); const UNSTABLE_virtual = (0, react_virtual_1.useVirtual)({ size: items.length, parentRef: containerRef, estimateSize: react_1.default.useCallback(() => UNSTABLE_defaultItemHeight, [UNSTABLE_defaultItemHeight]), horizontal: config.orientation === 'horizontal', overscan: 3, // overscan of 3 helps rapid navigation }); // Force Typescript to recognize the `Generic` symbol const genericState = { items: items.length ? items : staticItems, }; const state = { ...genericState, UNSTABLE_virtual, UNSTABLE_defaultItemHeight, containerRef, id, orientation, indexRef, nonInteractiveIds: config.nonInteractiveIds || [], isVirtualized, }; const events = { /** * Register an item to the list. Takes in an identifier, a React.Ref and an optional index. This * should be called on component mount. This event is only called for static rendering. */ registerItem(data) { indexRef.current++; setStaticItems(items => { return items.concat({ ...data, value: data, index: items.length, }); }); }, /** * Unregister an item by its identifier. This should be called when the component is unmounted. * This event is only called for static rendering. */ unregisterItem(data) { setStaticItems(items => { // this extra `if` ensures reference stability for no-ops if (items.find(item => item.id === data.id)) { return items.filter(item => item.id !== data.id); } else { return items; } }); }, /** * Updates the default item height. This should only be called when item height is measured. * Calling this with a different default item height than previous will cause a virtual list to * recalculate the overall height of the list and invalidate any height caching. Doing this only * on the first item may save the user from experiencing odd scrolling behavior where the * scrollbar updates while scrolling. If the user uses the mouse to drag the bar, it can become * "detached" since the browser recalculates scroll bar position while the overflow container is * updated. */ updateItemHeight(data) { setDefaultItemHeight(data.value); }, }; return { state, events, getId: config.getId }; });