UNPKG

@taiga-ui/cdk

Version:

Base library for creating Angular components and applications using Taiga UI principles regarding of actual visual appearance

236 lines 14.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.migrateMultiSelect = migrateMultiSelect; const insert_todo_1 = require("../../../../utils/insert-todo"); const elements_1 = require("../../../../utils/templates/elements"); const template_resource_1 = require("../../../../utils/templates/template-resource"); const control_state_attrs_1 = require("../../../utils/templates/control-state-attrs"); const replace_tag_1 = require("../../../utils/templates/replace-tag"); const DOCS_LINK = 'https://taiga-ui.dev/components/input-chip'; const VALUE_CONTENT_ATTR = 'valueContent'; const CONTENT_ATTR = 'content'; const TEXTFIELD_LABEL_OUTSIDE_ATTR = 'tuiTextfieldLabelOutside'; const AUTO_COLOR_ATTR = 'autoColor'; const AUTO_COLOR_TODO = `<!-- ${insert_todo_1.TODO_MARK} [autoColor] was removed. Use tuiChip with auto-color appearance instead. See https://taiga-ui.dev/components/chip#auto-color -->\n`; const PLACEHOLDER_ATTR = 'placeholder'; const PLACEHOLDER_BINDING_ATTR = '[placeholder]'; const EDITABLE_ATTR = 'editable'; const EDITABLE_BINDING_ATTR = '[editable]'; const TAG_VALIDATOR_BINDING_ATTR = '[tagValidator]'; const SEARCH_CHANGE_OUTPUT = '(searchChange)'; // [search] has no template-level v5 equivalent (requires viewChild + signal) const MANUAL_SEARCH_BINDING_ATTRS = new Set(['[(search)]', '[search]', 'search']); const CONTROL_ATTR_NAMES = [ 'formControlName', '[formControl]', 'formControl', '[(ngModel)]', '[ngModel]', 'ngModel', ]; const CONTROL_ATTRS = new Set(CONTROL_ATTR_NAMES.map((name) => name.toLowerCase())); function migrateMultiSelect({ resource, recorder, fileSystem, }) { const template = (0, template_resource_1.getTemplateFromTemplateResource)(resource, fileSystem); const templateOffset = (0, template_resource_1.getTemplateOffset)(resource); const elements = (0, elements_1.findElementsByTagName)(template, 'tui-multi-select'); elements.forEach((element) => { var _a, _b, _c, _d, _e, _f, _g, _h; const startOffset = (_a = element.sourceCodeLocation) === null || _a === void 0 ? void 0 : _a.startOffset; const controlAttrs = element.attrs.filter((attr) => CONTROL_ATTRS.has(attr.name.toLowerCase())); const controlStateAttrs = (0, control_state_attrs_1.getControlStateAttrs)(element); renameAttr(recorder, templateOffset, element, `[${VALUE_CONTENT_ATTR}]`, `[${CONTENT_ATTR}]`); renameAttr(recorder, templateOffset, element, VALUE_CONTENT_ATTR, CONTENT_ATTR); removeAttr(recorder, templateOffset, element, `[${TEXTFIELD_LABEL_OUTSIDE_ATTR}]`, template); removeAttr(recorder, templateOffset, element, TEXTFIELD_LABEL_OUTSIDE_ATTR, template); const hasAutoColor = element.attrs.some((attr) => [ `[${AUTO_COLOR_ATTR}]`.toLowerCase(), AUTO_COLOR_ATTR.toLowerCase(), ].includes(attr.name.toLowerCase())); removeAttr(recorder, templateOffset, element, `[${AUTO_COLOR_ATTR}]`, template); removeAttr(recorder, templateOffset, element, AUTO_COLOR_ATTR, template); if (hasAutoColor && typeof startOffset === 'number') { const lineStart = template.lastIndexOf('\n', startOffset) + 1; const indent = (_c = (_b = /^[ \t]*/.exec(template.slice(lineStart, startOffset))) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : ''; recorder.insertLeft(templateOffset + startOffset, `${AUTO_COLOR_TODO}${indent}`); } const placeholder = extractPlaceholder(element); removeAttr(recorder, templateOffset, element, PLACEHOLDER_ATTR, template); removeAttr(recorder, templateOffset, element, PLACEHOLDER_BINDING_ATTR, template); const selectLike = extractSelectLike(element); removeAttr(recorder, templateOffset, element, EDITABLE_ATTR, template); removeAttr(recorder, templateOffset, element, EDITABLE_BINDING_ATTR, template); // Extract [tagValidator] → <tui-input-chip *tuiItem="let ctx" [appearance]="..."> const tagValidatorExpr = extractTagValidator(element); if (tagValidatorExpr !== null) { removeAttr(recorder, templateOffset, element, TAG_VALIDATOR_BINDING_ATTR, template); } // (searchChange) → (input) on <input> with type narrowing const searchChangeExpr = extractSearchChange(element); if (searchChangeExpr !== null) { removeAttr(recorder, templateOffset, element, SEARCH_CHANGE_OUTPUT, template); } // [search] / [(search)] / search — no template-level v5 equivalent, remove from element const hasManualSearchAttrs = element.attrs.some((attr) => MANUAL_SEARCH_BINDING_ATTRS.has(attr.name.toLowerCase())); if (hasManualSearchAttrs) { for (const attrName of MANUAL_SEARCH_BINDING_ATTRS) { removeAttr(recorder, templateOffset, element, attrName, template); } } // Remove form control attrs from element (they go on <input>) controlAttrs.forEach((attr) => { removeAttr(recorder, templateOffset, element, attr.name, template); }); (0, control_state_attrs_1.removeControlStateAttrs)(recorder, templateOffset, element, template, controlStateAttrs); const hasChevron = element.attrs.some((attr) => attr.name.toLowerCase() === 'tuiChevron'.toLowerCase()); (0, replace_tag_1.replaceTag)(recorder, element.sourceCodeLocation, 'tui-multi-select', 'tui-textfield', template, templateOffset, ['multi', ...(hasChevron ? [] : ['tuiChevron'])]); // Check if <input> already exists inside const existingInputs = element.childNodes.filter((node) => node.nodeName === 'input'); if (existingInputs.length) { existingInputs.forEach((input) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; const hasInputChip = input.attrs.some((attr) => attr.name.toLowerCase() === 'tuiInputChip'.toLowerCase()); if (!hasInputChip) { const inputStartOffset = ((_e = (_c = (_b = (_a = input.sourceCodeLocation) === null || _a === void 0 ? void 0 : _a.startTag) === null || _b === void 0 ? void 0 : _b.startOffset) !== null && _c !== void 0 ? _c : (_d = input.sourceCodeLocation) === null || _d === void 0 ? void 0 : _d.startOffset) !== null && _e !== void 0 ? _e : 0) + '<input'.length; recorder.insertRight(templateOffset + inputStartOffset, ' tuiInputChip'); } const formAttrs = formatControlAttrs(controlAttrs); const controlStateStr = (0, control_state_attrs_1.stringifyControlStateAttrs)(controlStateAttrs); const extras = `${formAttrs ? ` ${formAttrs}` : ''}${controlStateStr}`; if (!extras) { return; } const insertOffset = ((_k = (_h = (_g = (_f = input.sourceCodeLocation) === null || _f === void 0 ? void 0 : _f.startTag) === null || _g === void 0 ? void 0 : _g.startOffset) !== null && _h !== void 0 ? _h : (_j = input.sourceCodeLocation) === null || _j === void 0 ? void 0 : _j.startOffset) !== null && _k !== void 0 ? _k : 0) + '<input'.length; recorder.insertRight(templateOffset + insertOffset, extras); }); return; } // Insert new <input tuiInputChip /> before first element child const formAttrs = formatControlAttrs(controlAttrs); const placeholderAttr = formatPlaceholderAttr(placeholder); const selectLikeAttr = selectLike === true ? ' tuiSelectLike' : ''; // selectLike is a string when [editable]="someExpr" was dynamic const readonlyAttr = typeof selectLike === 'string' ? ` [readOnly]="!${selectLike}"` : ''; const searchChangeAttr = searchChangeExpr ? ` (input)="${searchChangeExpr}(($event.target as HTMLInputElement).value)"` : ''; const manualSearchTodo = hasManualSearchAttrs ? `<!-- ${insert_todo_1.TODO_MARK} [search] was removed. Use (input) on <input tuiInputChip (input)="onSearch($any($event).target.value)"> to track changes; no direct equivalent for programmatic writes. See: ${DOCS_LINK} -->\n` : ''; const searchChangeTodo = searchChangeExpr ? `<!-- ${insert_todo_1.TODO_MARK} (searchChange) was replaced by (input) on <input tuiInputChip (input)="onSearch($any($event).target.value)">. See: ${DOCS_LINK} -->\n` : ''; const firstElementChildOffset = (_e = (_d = element.childNodes.find((node) => node.nodeName !== '#text')) === null || _d === void 0 ? void 0 : _d.sourceCodeLocation) === null || _e === void 0 ? void 0 : _e.startOffset; const insertOffset = (_h = firstElementChildOffset !== null && firstElementChildOffset !== void 0 ? firstElementChildOffset : (_g = (_f = element.sourceCodeLocation) === null || _f === void 0 ? void 0 : _f.endTag) === null || _g === void 0 ? void 0 : _g.startOffset) !== null && _h !== void 0 ? _h : 0; const chipItemTemplate = tagValidatorExpr === null ? '' : `<tui-input-chip *tuiItem="let ctx" [appearance]="${tagValidatorExpr}(ctx.item) ? '' : 'negative'" />\n`; const controlStateStr = (0, control_state_attrs_1.stringifyControlStateAttrs)(controlStateAttrs); const inputTemplate = `${manualSearchTodo}${searchChangeTodo}<input tuiInputChip${selectLikeAttr}${readonlyAttr}${placeholderAttr ? ` ${placeholderAttr}` : ''}${formAttrs ? ` ${formAttrs}` : ''}${searchChangeAttr}${controlStateStr} />\n${chipItemTemplate}`; recorder.insertRight(templateOffset + insertOffset, inputTemplate); }); } /** * Returns: * true — [editable]="false" → add tuiSelectLike * false — [editable]="true" or absent → nothing to do * string (expr) — [editable]="someExpr" → add [readOnly]="!someExpr" on <input> */ function extractSelectLike(element) { const bindingAttr = element.attrs.find((attr) => attr.name.toLowerCase() === EDITABLE_BINDING_ATTR); if (bindingAttr) { if (bindingAttr.value === 'false') { return true; } if (bindingAttr.value === 'true') { return false; } // Dynamic expression — return it so caller can generate [readOnly]="!expr" return bindingAttr.value; } const plainAttr = element.attrs.find((attr) => attr.name.toLowerCase() === EDITABLE_ATTR); return plainAttr ? plainAttr.value === 'false' : false; } /** * Extracts the handler expression from (searchChange)="handler($event)". * Returns just "handler" so we can generate (input)="handler(($event.target as HTMLInputElement).value)". * Returns null if the attr is absent. */ function extractSearchChange(element) { const attr = element.attrs.find((a) => a.name.toLowerCase() === SEARCH_CHANGE_OUTPUT.toLowerCase()); if (!attr) { return null; } // Strip trailing ($event) call if present: "fn($event)" → "fn" return attr.value.replace(/\(\$event\)\s*$/, '').trim(); } /** * Extracts the expression from [tagValidator]="expr". * Returns the expression string, or null if attr is absent. * The expression is used to generate: * <tui-input-chip *tuiItem="let ctx" [appearance]="expr(ctx.item) ? '' : 'negative'" /> */ function extractTagValidator(element) { var _a; const attr = element.attrs.find((a) => a.name.toLowerCase() === TAG_VALIDATOR_BINDING_ATTR.toLowerCase()); return (_a = attr === null || attr === void 0 ? void 0 : attr.value) !== null && _a !== void 0 ? _a : null; } function extractPlaceholder(element) { const bindingAttr = element.attrs.find((attr) => attr.name.toLowerCase() === PLACEHOLDER_BINDING_ATTR); if (bindingAttr) { return { attr: PLACEHOLDER_BINDING_ATTR, value: bindingAttr.value }; } const plainAttr = element.attrs.find((attr) => attr.name.toLowerCase() === PLACEHOLDER_ATTR); return plainAttr ? { attr: PLACEHOLDER_ATTR, value: plainAttr.value } : null; } function formatPlaceholderAttr(placeholder) { return placeholder ? `${placeholder.attr}="${placeholder.value}"` : ''; } function renameAttr(recorder, templateOffset, element, from, to) { var _a, _b; const attrLocation = (_b = (_a = element.sourceCodeLocation) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b[from.toLowerCase()]; if (!attrLocation) { return; } recorder.remove(templateOffset + attrLocation.startOffset, from.length); recorder.insertRight(templateOffset + attrLocation.startOffset, to); } function removeAttr(recorder, templateOffset, element, name, template) { var _a, _b; const attrLocation = (_b = (_a = element.sourceCodeLocation) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b[name.toLowerCase()]; if (!attrLocation) { return; } const lineStart = template.lastIndexOf('\n', attrLocation.startOffset) + 1; const lineEnd = template.indexOf('\n', attrLocation.endOffset); const attrText = template.slice(attrLocation.startOffset, attrLocation.endOffset); const lineText = lineEnd === -1 ? '' : template.slice(lineStart, lineEnd); if (lineText.trim() === attrText.trim() && lineEnd !== -1) { recorder.remove(templateOffset + lineStart, lineEnd - lineStart + 1); return; } recorder.remove(templateOffset + attrLocation.startOffset - 1, attrLocation.endOffset - attrLocation.startOffset + 1); } function formatControlAttrs(attrs) { return attrs .map(({ name, value }) => `${normalizeAttrName(name)}="${value}"`) .join(' '); } function normalizeAttrName(name) { switch (name.toLowerCase()) { case '[formControl]'.toLowerCase(): return '[formControl]'; case '[ngModel]'.toLowerCase(): return '[ngModel]'; case 'formControl'.toLowerCase(): return 'formControl'; case 'formControlName'.toLowerCase(): return 'formControlName'; case '[(ngmodel)]': return '[(ngModel)]'; case 'ngmodel': return 'ngModel'; default: return name; } } //# sourceMappingURL=migrate-multi-select.js.map