@luscii-healthtech/web-ui 47.0.4 → 48.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ import type { API, FileInfo } from "jscodeshift";
2
+ export default function transformer(file: FileInfo, api: API): string;
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ // Codemod for Tag component breaking changes.
3
+ //
4
+ // Handles:
5
+ // - colorTheme="red"|"amber"|"green"|"gray"|"blue" → _type="danger"|"negative"|"positive"|"neutral"|"info"
6
+ // - size="base" → _size="large", size="small" → _size="small"
7
+ // - variant → _variant
8
+ // - truncate → _truncate
9
+ // - text="..." → children (prop removed, value becomes children)
10
+ // - showIcon → _leadingIcon with a TODO comment (boolean → ReactNode, can't auto-migrate)
11
+ // - as → _as (on direct <Tag> elements only)
12
+ // - Also handles polymorphic usage via `as={Tag}` (e.g. <Stack as={Tag} variant="bordered" />),
13
+ // transforming Tag-specific props on the parent element (but not renaming `as` itself)
14
+ //
15
+ // Usage: npx jscodeshift --parser=tsx --extensions=tsx,ts -t @luscii-healthtech/web-ui/dist/codemods/v48-tag-props-update <target-dir>
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.default = transformer;
18
+ const COLOR_THEME_TO_TYPE = {
19
+ red: "danger",
20
+ amber: "negative",
21
+ green: "positive",
22
+ gray: "neutral",
23
+ blue: "info",
24
+ };
25
+ // Props that simply get an underscore prefix (no value change)
26
+ const RENAMED_PROPS = {
27
+ type: "_type",
28
+ variant: "_variant",
29
+ truncate: "_truncate",
30
+ };
31
+ /**
32
+ * Transform Tag-specific props on a JSX opening element.
33
+ * Returns true if any changes were made.
34
+ */
35
+ function transformTagProps(opening, j) {
36
+ var _a, _b, _c, _d, _e, _f;
37
+ let changed = false;
38
+ // --- colorTheme → _type (with value mapping) ---
39
+ const colorThemeAttr = findAttribute(opening, "colorTheme");
40
+ if (colorThemeAttr) {
41
+ colorThemeAttr.name = j.jsxIdentifier("_type");
42
+ if (((_a = colorThemeAttr.value) === null || _a === void 0 ? void 0 : _a.type) === "StringLiteral") {
43
+ const mapped = COLOR_THEME_TO_TYPE[colorThemeAttr.value.value];
44
+ if (mapped) {
45
+ colorThemeAttr.value = j.stringLiteral(mapped);
46
+ }
47
+ }
48
+ else if (((_b = colorThemeAttr.value) === null || _b === void 0 ? void 0 : _b.type) === "JSXExpressionContainer" &&
49
+ colorThemeAttr.value.expression.type === "StringLiteral") {
50
+ const mapped = COLOR_THEME_TO_TYPE[colorThemeAttr.value.expression.value];
51
+ if (mapped) {
52
+ colorThemeAttr.value = j.stringLiteral(mapped);
53
+ }
54
+ }
55
+ changed = true;
56
+ }
57
+ // --- size → _size (with "base" → "large") ---
58
+ const sizeAttr = findAttribute(opening, "size");
59
+ if (sizeAttr) {
60
+ sizeAttr.name = j.jsxIdentifier("_size");
61
+ if (((_c = sizeAttr.value) === null || _c === void 0 ? void 0 : _c.type) === "StringLiteral") {
62
+ if (sizeAttr.value.value === "base") {
63
+ sizeAttr.value = j.stringLiteral("large");
64
+ }
65
+ }
66
+ else if (((_d = sizeAttr.value) === null || _d === void 0 ? void 0 : _d.type) === "JSXExpressionContainer" &&
67
+ sizeAttr.value.expression.type === "StringLiteral" &&
68
+ sizeAttr.value.expression.value === "base") {
69
+ sizeAttr.value = j.stringLiteral("large");
70
+ }
71
+ changed = true;
72
+ }
73
+ // --- Simple renames (variant → _variant, truncate → _truncate) ---
74
+ for (const [oldName, newName] of Object.entries(RENAMED_PROPS)) {
75
+ const attr = findAttribute(opening, oldName);
76
+ if (attr) {
77
+ attr.name = j.jsxIdentifier(newName);
78
+ changed = true;
79
+ }
80
+ }
81
+ // --- showIcon → _leadingIcon with TODO ---
82
+ const showIconAttr = findAttribute(opening, "showIcon");
83
+ if (showIconAttr) {
84
+ const attrIndex = (_f = (_e = opening.attributes) === null || _e === void 0 ? void 0 : _e.indexOf(showIconAttr)) !== null && _f !== void 0 ? _f : -1;
85
+ if (attrIndex !== -1 && opening.attributes) {
86
+ opening.attributes[attrIndex] = j.jsxAttribute(j.jsxIdentifier("_leadingIcon"), j.jsxExpressionContainer(j.identifier("undefined /* TODO: showIcon was removed — pass a ReactNode icon to _leadingIcon instead */")));
87
+ }
88
+ changed = true;
89
+ }
90
+ return changed;
91
+ }
92
+ function transformer(file, api) {
93
+ const j = api.jscodeshift;
94
+ const root = j(file.source);
95
+ let hasChanges = false;
96
+ // --- Pass 1: Direct <Tag> elements ---
97
+ root.findJSXElements("Tag").forEach((path) => {
98
+ var _a;
99
+ const opening = path.value.openingElement;
100
+ if (transformTagProps(opening, j)) {
101
+ hasChanges = true;
102
+ }
103
+ // --- as → _as (Tag's own polymorphic prop) ---
104
+ const asAttr = findAttribute(opening, "as");
105
+ if (asAttr) {
106
+ asAttr.name = j.jsxIdentifier("_as");
107
+ hasChanges = true;
108
+ }
109
+ // --- text="..." → children (only for direct <Tag> usage) ---
110
+ const textAttr = findAttribute(opening, "text");
111
+ if (textAttr) {
112
+ const textValue = textAttr.value;
113
+ // Remove the text attribute
114
+ opening.attributes = (_a = opening.attributes) === null || _a === void 0 ? void 0 : _a.filter((a) => {
115
+ if (a.type !== "JSXAttribute") {
116
+ return true;
117
+ }
118
+ return a.name.type === "JSXIdentifier" && a.name.name !== "text";
119
+ });
120
+ // Convert self-closing to open/close if needed
121
+ if (path.value.openingElement.selfClosing) {
122
+ path.value.openingElement.selfClosing = false;
123
+ path.value.closingElement = j.jsxClosingElement(j.jsxIdentifier("Tag"));
124
+ }
125
+ if ((textValue === null || textValue === void 0 ? void 0 : textValue.type) === "StringLiteral") {
126
+ path.value.children = [j.jsxText(textValue.value)];
127
+ }
128
+ else if ((textValue === null || textValue === void 0 ? void 0 : textValue.type) === "JSXExpressionContainer") {
129
+ path.value.children = [textValue];
130
+ }
131
+ hasChanges = true;
132
+ }
133
+ });
134
+ // --- Pass 2: Polymorphic as={Tag} usage (e.g. <Stack as={Tag} variant="bordered" />) ---
135
+ root.find(j.JSXOpeningElement).forEach((path) => {
136
+ var _a;
137
+ const opening = path.value;
138
+ const asAttr = findAttribute(opening, "as");
139
+ if (!asAttr) {
140
+ return;
141
+ }
142
+ // Check for as={Tag}
143
+ const isTagAs = ((_a = asAttr.value) === null || _a === void 0 ? void 0 : _a.type) === "JSXExpressionContainer" &&
144
+ asAttr.value.expression.type === "Identifier" &&
145
+ asAttr.value.expression.name === "Tag";
146
+ if (!isTagAs) {
147
+ return;
148
+ }
149
+ if (transformTagProps(opening, j)) {
150
+ hasChanges = true;
151
+ }
152
+ });
153
+ if (!hasChanges) {
154
+ return file.source;
155
+ }
156
+ return root.toSource({ quote: "double" });
157
+ }
158
+ function findAttribute(opening, name) {
159
+ var _a;
160
+ return (_a = opening.attributes) === null || _a === void 0 ? void 0 : _a.find((attr) => attr.type === "JSXAttribute" &&
161
+ attr.name.type === "JSXIdentifier" &&
162
+ attr.name.name === name);
163
+ }
@@ -256,6 +256,7 @@ const newVariantClasses = {
256
256
  "body-small-bold": "ui-typo-body-small-bold"
257
257
  };
258
258
  const newTextColorClasses = {
259
+ current: "ui:text-current",
259
260
  neutral: "ui:text-text-neutral-primary-default",
260
261
  "neutral-secondary": "ui:text-text-neutral-secondary-default",
261
262
  brand: "ui:text-text-brand-primary-default",
@@ -2098,29 +2099,29 @@ const Modal = (props) => {
2098
2099
  Modal.setAppElement = ReactModal__default.default.setAppElement;
2099
2100
 
2100
2101
  const Tag = (_a) => {
2101
- var { text, colorTheme = "gray", className, size = "base", variant = "flat", showIcon, truncate, children } = _a, rest = __rest(_a, ["text", "colorTheme", "className", "size", "variant", "showIcon", "truncate", "children"]);
2102
- const isBordered = variant === "bordered" || "";
2103
- return jsxRuntime.jsxs(Stack, Object.assign({ axis: "x", gap: "xxxxs", className: classNames__default.default("ui:inline-flex ui:items-center ui:rounded-lg ui:px-2 ui:py-1", { "overflow-hidden": truncate }, className, {
2102
+ var { _as = "span", _type = "neutral", _size = "large", _variant = "flat", _leadingIcon, _trailingIcon, _truncate, children, className } = _a, rest = __rest(_a, ["_as", "_type", "_size", "_variant", "_leadingIcon", "_trailingIcon", "_truncate", "children", "className"]);
2103
+ const isBordered = _variant === "bordered";
2104
+ return jsxRuntime.jsxs(Stack, Object.assign({ as: _as, axis: "x", align: "center", borderRadius: "xxxxs", _gap: "s", className: classNames__default.default("ui:inline-flex", { "overflow-hidden": _truncate }, className, {
2104
2105
  [[
2105
- "ui:bg-negative-background ui:text-on-negative",
2106
- isBordered && "ui:border-negative-border"
2107
- ].join(" ")]: colorTheme === "red",
2106
+ "ui:bg-background-error-primary-default ui:text-text-error-primary-default",
2107
+ isBordered && "ui:border-border-error-primary-default"
2108
+ ].filter(Boolean).join(" ")]: _type === "danger",
2108
2109
  [[
2109
- "ui:bg-warning-background ui:text-on-warning",
2110
- isBordered && "ui:border-warning-border"
2111
- ].join(" ")]: colorTheme === "amber",
2110
+ "ui:bg-background-warning-primary-default ui:text-text-warning-primary-default",
2111
+ isBordered && "ui:border-border-warning-primary-default"
2112
+ ].filter(Boolean).join(" ")]: _type === "negative",
2112
2113
  [[
2113
- "ui:bg-positive-background ui:text-on-positive",
2114
- isBordered && "ui:border-positive-border"
2115
- ].join(" ")]: colorTheme === "green",
2114
+ "ui:bg-background-success-primary-default ui:text-text-success-primary-default",
2115
+ isBordered && "ui:border-border-success-primary-default"
2116
+ ].filter(Boolean).join(" ")]: _type === "positive",
2116
2117
  [[
2117
- "ui:bg-neutral-background ui:text-on-surface",
2118
- isBordered && "ui:border-neutral-border-high-contrast"
2119
- ].join(" ")]: colorTheme === "gray",
2118
+ "ui:bg-background-neutral-primary-default ui:text-text-neutral-primary-default",
2119
+ isBordered && "ui:border-border-neutral-primary-default"
2120
+ ].filter(Boolean).join(" ")]: _type === "neutral",
2120
2121
  [[
2121
- "ui:bg-primary-background ui:text-primary",
2122
- isBordered && "ui:border-primary-border"
2123
- ].join(" ")]: colorTheme === "blue"
2122
+ "ui:bg-background-info-primary-default ui:text-text-info-primary-default",
2123
+ isBordered && "ui:border-border-info-primary-default"
2124
+ ].filter(Boolean).join(" ")]: _type === "info"
2124
2125
  }, {
2125
2126
  /**
2126
2127
  * Adding a border adds it to the outside of an element. This means that the border will
@@ -2128,36 +2129,19 @@ const Tag = (_a) => {
2128
2129
  * variants have the same dimensions, we need to adjust the padding to account for
2129
2130
  * the border width.
2130
2131
  */
2131
- "ui:border ui:px-[calc(--spacing(2)-1px)] ui:py-[calc(--spacing(1)-1px)]": variant === "bordered"
2132
- }) }, rest, { children: [showIcon && jsxRuntime.jsx(TagIcon, { colorTheme, size }), jsxRuntime.jsx(
2133
- Text,
2134
- {
2135
- /**
2136
- * Ensures the `small` size, with smaller text, is the same height as the `base` size.
2137
- */
2138
- className: "ui:leading-5",
2139
- color: "current",
2140
- variant: size === "small" ? "sm-strong" : "base",
2141
- truncate,
2142
- children: text !== null && text !== void 0 ? text : children
2143
- }
2144
- )] }));
2132
+ "ui:px-f_base ui:py-f_s": _variant !== "bordered",
2133
+ "ui:border ui:px-[calc(var(--ui-semantic-spacing-base)-1px)] ui:py-[calc(var(--ui-semantic-spacing-s)-1px)]": _variant === "bordered"
2134
+ }) }, rest, { children: [sizedIcon(_leadingIcon, { size: _size }), jsxRuntime.jsx(Text, { color: "current", variant: _size === "small" ? "body-small-regular" : "body-base-regular", truncate: _truncate, children }), sizedIcon(_trailingIcon, { size: _size })] }));
2145
2135
  };
2146
- function TagIcon({ colorTheme, size }) {
2147
- const sizeClass = classNames__default.default({ "ui:h-[16px] ui:w-[16px]": size === "small" });
2148
- const props = { size: "s" };
2149
- switch (colorTheme) {
2150
- case "red":
2151
- return jsxRuntime.jsx(SmallCircleIcon, Object.assign({}, props, { className: sizeClass }));
2152
- case "amber":
2153
- return jsxRuntime.jsx(SmallSquareIcon, Object.assign({}, props, { className: sizeClass }));
2154
- case "green":
2155
- return jsxRuntime.jsx(SmallArrowUpIcon, Object.assign({}, props, { className: classNames__default.default(sizeClass, "ui:rotate-90") }));
2156
- case "gray":
2157
- return jsxRuntime.jsx(SmallDiamondIcon, Object.assign({}, props, { className: classNames__default.default(sizeClass, "ui:text-on-surface-variant") }));
2158
- case "blue":
2159
- return jsxRuntime.jsx(SmallArrowUpIcon, Object.assign({}, props, { className: classNames__default.default(sizeClass, "ui:rotate-90") }));
2136
+ function sizedIcon(node, options) {
2137
+ const pixelSize = options.size === "small" ? 16 : 24;
2138
+ if (!React.isValidElement(node)) {
2139
+ return null;
2160
2140
  }
2141
+ return jsxRuntime.jsx(Box, { className: "ui:shrink-0", children: React.cloneElement(node, {
2142
+ width: pixelSize,
2143
+ height: pixelSize
2144
+ }) });
2161
2145
  }
2162
2146
 
2163
2147
  function ChatBubble(props) {
@@ -2208,7 +2192,7 @@ function ChatBubbleText(props) {
2208
2192
  }
2209
2193
  ChatBubble.Text = ChatBubbleText;
2210
2194
  function ChatBubbleDateIndicator(props) {
2211
- return jsxRuntime.jsx("div", { className: "ui:flex ui:justify-center", children: jsxRuntime.jsx(Tag, { colorTheme: "gray", size: "small", className: "ui:py-2", children: props.children }) });
2195
+ return jsxRuntime.jsx("div", { className: "ui:flex ui:justify-center", children: jsxRuntime.jsx(Tag, { _type: "neutral", _size: "small", className: "ui:py-2", children: props.children }) });
2212
2196
  }
2213
2197
  ChatBubble.DateIndicator = ChatBubbleDateIndicator;
2214
2198
 
@@ -3554,7 +3538,7 @@ function TableBodyRowDataCell(props) {
3554
3538
  }
3555
3539
  const content = props.fieldConfig.content(props.item);
3556
3540
  const isTextCellEmpty = isTableFieldText(content) && !content.text && !content.tag;
3557
- return jsxRuntime.jsxs("td", { className: classNames__default.default("ui:px-2 ui:align-middle ui:first:pl-6 ui:last:pr-6"), children: [isTableFieldText(content) && jsxRuntime.jsxs(Stack, { axis: "x", align: "center", gap: "xs", children: [content.tag && jsxRuntime.jsx(Tag, { colorTheme: content.tag.color, children: content.tag.text }), jsxRuntime.jsx(CellTextContent, { content, emptyFieldContent: isTextCellEmpty ? props.emptyFieldContentText : void 0 })] }), isTableFieldAction(content) && jsxRuntime.jsx("div", { className: classNames__default.default("ui:flex ui:flex-row ui:justify-end", "ui:transition-opacity ui:duration-300 ui:ease-in-out", {
3541
+ return jsxRuntime.jsxs("td", { className: classNames__default.default("ui:px-2 ui:align-middle ui:first:pl-6 ui:last:pr-6"), children: [isTableFieldText(content) && jsxRuntime.jsxs(Stack, { axis: "x", align: "center", gap: "xs", children: [content.tag && jsxRuntime.jsx(Tag, { _type: getTagType(content.tag.color), children: content.tag.text }), jsxRuntime.jsx(CellTextContent, { content, emptyFieldContent: isTextCellEmpty ? props.emptyFieldContentText : void 0 })] }), isTableFieldAction(content) && jsxRuntime.jsx("div", { className: classNames__default.default("ui:flex ui:flex-row ui:justify-end", "ui:transition-opacity ui:duration-300 ui:ease-in-out", {
3558
3542
  "ui:opacity-0": props.fieldConfig.onlyShowContentOnHovering,
3559
3543
  "ui:group-hover:opacity-100": props.fieldConfig.onlyShowContentOnHovering
3560
3544
  }), children: content.map((action) => {
@@ -3579,6 +3563,22 @@ const CellTextContent = (props) => {
3579
3563
  "ui:break-words": !props.content.description.breakAllWord
3580
3564
  }), variant: "sm", color: "slate-500", children: props.content.description.content })] });
3581
3565
  };
3566
+ function getTagType(color) {
3567
+ switch (color) {
3568
+ case "red":
3569
+ return "danger";
3570
+ case "amber":
3571
+ return "negative";
3572
+ case "green":
3573
+ return "positive";
3574
+ case "gray":
3575
+ return "neutral";
3576
+ case "blue":
3577
+ return "info";
3578
+ default:
3579
+ return "neutral";
3580
+ }
3581
+ }
3582
3582
 
3583
3583
  function TableBody(props) {
3584
3584
  var _a;
@@ -5192,7 +5192,7 @@ const MediaPicker = React__namespace.default.forwardRef((props, ref) => {
5192
5192
  return jsxRuntime.jsx(ImagePickerInner, Object.assign({}, mappedProps, { innerRef: ref }));
5193
5193
  });
5194
5194
 
5195
- const TagGroup = ({ children, tags, tagSize = "base", className }) => jsxRuntime.jsxs("div", { className: classNames__default.default("ui:flex ui:flex-row ui:flex-wrap ui:gap-2", className), children: [children, tags === null || tags === void 0 ? void 0 : tags.map((tag) => jsxRuntime.jsx(Tag, Object.assign({ size: tagSize }, tag)))] });
5195
+ const TagGroup = ({ children, tags, tagSize = "large", className }) => jsxRuntime.jsxs("div", { className: classNames__default.default("ui:flex ui:flex-row ui:flex-wrap ui:gap-2", className), children: [children, tags === null || tags === void 0 ? void 0 : tags.map((tag) => jsxRuntime.jsx(Tag, Object.assign({ _size: tagSize }, tag)))] });
5196
5196
 
5197
5197
  var css_248z$2 = "/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */\n.cweb-textarea {\n resize: none;\n background-color: field;\n}\n.cweb-textarea.has-icon {\n background-size: 24px;\n background-position: 10px 10px;\n background-repeat: no-repeat;\n padding-left: 44px;\n}\n.cweb-textarea.resizable {\n resize: both;\n}\n.cweb-textarea.resizable-x {\n resize: horizontal;\n}\n.cweb-textarea.resizable-y {\n resize: vertical;\n}\n.cweb-textarea:focus {\n border-color: var(--color-primary);\n}\n.cweb-textarea:disabled {\n cursor: not-allowed;\n}\n.cweb-textarea.has-error {\n border: 1px solid #b91c1c;\n color: #991b1b;\n}";
5198
5198
  styleInject(css_248z$2);