@moodlehq/design-system 3.2.0 → 4.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,19 @@
1
+ import { HTMLAttributes, ReactElement } from 'react';
2
+ type BadgeVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info';
3
+ type IconElement = ReactElement<'i' | 'svg'>;
4
+ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
5
+ /** Visible badge text. Must be a caller-supplied translated string. */
6
+ label: string;
7
+ /** Colour/semantic variant. Defaults to `primary`. */
8
+ variant?: BadgeVariant;
9
+ /** When true, renders the low-contrast (subtle) style with a light background and border. */
10
+ subtle?: boolean;
11
+ /** When true, renders fully rounded pill shape instead of the default slight rounding. */
12
+ pill?: boolean;
13
+ /** Optional icon rendered before the label. Must be an `<i>` or `<svg>` element. Mutually exclusive with `endIcon`. */
14
+ startIcon?: IconElement;
15
+ /** Optional icon rendered after the label. Must be an `<i>` or `<svg>` element. Mutually exclusive with `startIcon`. */
16
+ endIcon?: IconElement;
17
+ }
18
+ export declare const Badge: ({ label, variant, subtle, pill, startIcon, endIcon, className, ...props }: BadgeProps) => import("react/jsx-runtime").JSX.Element;
19
+ export {};
@@ -0,0 +1,43 @@
1
+ /* empty css */
2
+ import { isValidElement } from "react";
3
+ import { jsxs } from "react/jsx-runtime";
4
+ //#region components/badge/Badge.tsx
5
+ var isIconElement = (el, propName) => {
6
+ return isValidElement(el) && (el.type === "i" || el.type === "svg");
7
+ };
8
+ var allowedVariants = [
9
+ "primary",
10
+ "secondary",
11
+ "success",
12
+ "danger",
13
+ "warning",
14
+ "info"
15
+ ];
16
+ var Badge = ({ label, variant, subtle = false, pill = false, startIcon, endIcon, className, ...props }) => {
17
+ const resolvedVariant = variant && allowedVariants.includes(variant) ? variant : "primary";
18
+ const resolvedStartIcon = isIconElement(startIcon, "startIcon") ? startIcon : null;
19
+ let resolvedEndIcon = isIconElement(endIcon, "endIcon") ? endIcon : null;
20
+ if (resolvedStartIcon && resolvedEndIcon) resolvedEndIcon = null;
21
+ const classes = [
22
+ "mds-badge",
23
+ "badge",
24
+ `mds-badge--${resolvedVariant}`
25
+ ];
26
+ if (resolvedStartIcon || resolvedEndIcon) classes.push("mds-badge--has-icon");
27
+ if (subtle) classes.push("mds-badge--subtle");
28
+ if (pill) classes.push("mds-badge--pill");
29
+ if (className) classes.push(className);
30
+ return /* @__PURE__ */ jsxs("span", {
31
+ className: classes.join(" "),
32
+ ...props,
33
+ children: [
34
+ resolvedStartIcon,
35
+ label,
36
+ resolvedEndIcon
37
+ ]
38
+ });
39
+ };
40
+ //#endregion
41
+ export { Badge };
42
+
43
+ //# sourceMappingURL=Badge2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Badge2.js","names":[],"sources":["../../../components/badge/Badge.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactElement } from 'react';\nimport { isValidElement } from 'react';\nimport './badge.css';\n\ntype BadgeVariant =\n | 'primary'\n | 'secondary'\n | 'success'\n | 'danger'\n | 'warning'\n | 'info';\n\ntype IconElement = ReactElement<'i' | 'svg'>;\n\n// Runtime guard — icon props must be <i> or <svg> elements\nconst isIconElement = (el: unknown, propName: string): el is IconElement => {\n const valid = isValidElement(el) && (el.type === 'i' || el.type === 'svg');\n if (!valid && el != null && import.meta.env.DEV) {\n console.error(`Badge: \\`${propName}\\` must be an <i> or <svg> element.`);\n }\n return valid;\n};\n\nconst allowedVariants: BadgeVariant[] = [\n 'primary',\n 'secondary',\n 'success',\n 'danger',\n 'warning',\n 'info',\n];\n\nexport interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n /** Visible badge text. Must be a caller-supplied translated string. */\n label: string;\n /** Colour/semantic variant. Defaults to `primary`. */\n variant?: BadgeVariant;\n /** When true, renders the low-contrast (subtle) style with a light background and border. */\n subtle?: boolean;\n /** When true, renders fully rounded pill shape instead of the default slight rounding. */\n pill?: boolean;\n /** Optional icon rendered before the label. Must be an `<i>` or `<svg>` element. Mutually exclusive with `endIcon`. */\n startIcon?: IconElement;\n /** Optional icon rendered after the label. Must be an `<i>` or `<svg>` element. Mutually exclusive with `startIcon`. */\n endIcon?: IconElement;\n}\n\nexport const Badge = ({\n label,\n variant,\n subtle = false,\n pill = false,\n startIcon,\n endIcon,\n className,\n ...props\n}: BadgeProps) => {\n const resolvedVariant =\n variant && allowedVariants.includes(variant as BadgeVariant)\n ? variant\n : 'primary';\n\n const resolvedStartIcon = isIconElement(startIcon, 'startIcon')\n ? startIcon\n : null;\n let resolvedEndIcon = isIconElement(endIcon, 'endIcon') ? endIcon : null;\n\n if (import.meta.env.DEV) {\n if (variant && !allowedVariants.includes(variant as BadgeVariant)) {\n console.warn(\n `[MDS Badge] Invalid variant \"${variant}\". Falling back to \"primary\". Allowed: ${allowedVariants.join(', ')}`,\n );\n }\n if (resolvedStartIcon && resolvedEndIcon) {\n console.warn(\n '[MDS Badge] `startIcon` and `endIcon` are mutually exclusive. Rendering `startIcon` only.',\n );\n }\n }\n\n // Only one icon can be rendered at a time; startIcon takes precedence when both are provided.\n if (resolvedStartIcon && resolvedEndIcon) {\n resolvedEndIcon = null;\n }\n\n const classes = ['mds-badge', 'badge', `mds-badge--${resolvedVariant}`];\n if (resolvedStartIcon || resolvedEndIcon) classes.push('mds-badge--has-icon');\n if (subtle) classes.push('mds-badge--subtle');\n if (pill) classes.push('mds-badge--pill');\n if (className) classes.push(className);\n\n return (\n <span className={classes.join(' ')} {...props}>\n {resolvedStartIcon}\n {label}\n {resolvedEndIcon}\n </span>\n );\n};\n"],"mappings":";;;;AAeA,IAAM,iBAAiB,IAAa,aAAwC;CAK1E,OAJc,eAAe,EAAE,MAAM,GAAG,SAAS,OAAO,GAAG,SAAS;AAKtE;AAEA,IAAM,kBAAkC;CACtC;CACA;CACA;CACA;CACA;CACA;AACF;AAiBA,IAAa,SAAS,EACpB,OACA,SACA,SAAS,OACT,OAAO,OACP,WACA,SACA,WACA,GAAG,YACa;CAChB,MAAM,kBACJ,WAAW,gBAAgB,SAAS,OAAuB,IACvD,UACA;CAEN,MAAM,oBAAoB,cAAc,WAAW,WAAW,IAC1D,YACA;CACJ,IAAI,kBAAkB,cAAc,SAAS,SAAS,IAAI,UAAU;CAgBpE,IAAI,qBAAqB,iBACvB,kBAAkB;CAGpB,MAAM,UAAU;EAAC;EAAa;EAAS,cAAc;CAAiB;CACtE,IAAI,qBAAqB,iBAAiB,QAAQ,KAAK,qBAAqB;CAC5E,IAAI,QAAQ,QAAQ,KAAK,mBAAmB;CAC5C,IAAI,MAAM,QAAQ,KAAK,iBAAiB;CACxC,IAAI,WAAW,QAAQ,KAAK,SAAS;CAErC,OACE,qBAAC,QAAD;EAAM,WAAW,QAAQ,KAAK,GAAG;EAAG,GAAI;YAAxC;GACG;GACA;GACA;EACG;;AAEV"}
@@ -0,0 +1 @@
1
+ export * from './Badge';
@@ -0,0 +1,2 @@
1
+ import { Badge } from "./Badge2.js";
2
+ export { Badge };
@@ -1,12 +1,13 @@
1
1
  import { ButtonHTMLAttributes, ReactElement } from 'react';
2
- type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'outline-primary' | 'outline-secondary' | 'outline-danger';
2
+ type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost' | 'outline-primary' | 'outline-secondary' | 'outline-danger';
3
+ type ButtonSize = 'sm' | 'md' | 'lg';
3
4
  type IconElement = ReactElement<'i' | 'svg'>;
4
5
  export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
5
6
  label?: string;
6
7
  variant?: ButtonVariant;
7
- size?: 'sm' | 'lg';
8
+ size?: ButtonSize;
8
9
  startIcon?: IconElement;
9
10
  endIcon?: IconElement;
10
11
  }
11
- export declare const Button: ({ label, variant, size, startIcon, endIcon, className, type, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
12
+ export declare const Button: import('react').ForwardRefExoticComponent<ButtonProps & import('react').RefAttributes<HTMLButtonElement>>;
12
13
  export {};
@@ -1,5 +1,5 @@
1
- import { isValidElement } from "react";
2
- import { jsxs } from "react/jsx-runtime";
1
+ import { forwardRef, isValidElement } from "react";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  //#region components/button/Button.tsx
4
4
  var isIconElement = (el, propName) => {
5
5
  return isValidElement(el) && (el.type === "i" || el.type === "svg");
@@ -8,29 +8,43 @@ var allowedVariants = [
8
8
  "primary",
9
9
  "secondary",
10
10
  "danger",
11
+ "ghost",
11
12
  "outline-primary",
12
13
  "outline-secondary",
13
14
  "outline-danger"
14
15
  ];
15
- var Button = ({ label, variant, size, startIcon, endIcon, className, type = "button", ...props }) => {
16
+ var allowedSizes = [
17
+ "sm",
18
+ "md",
19
+ "lg"
20
+ ];
21
+ var Button = forwardRef(function Button({ label, variant, size, startIcon, endIcon, className, type = "button", ...props }, ref) {
22
+ const resolvedVariant = variant && allowedVariants.includes(variant) ? variant : "primary";
23
+ const resolvedSize = size && allowedSizes.includes(size) ? size : "md";
24
+ const resolvedStartIcon = isIconElement(startIcon, "startIcon") ? startIcon : null;
25
+ const resolvedEndIcon = isIconElement(endIcon, "endIcon") ? endIcon : null;
26
+ const isIconOnly = !label && Boolean(resolvedStartIcon || resolvedEndIcon);
16
27
  const classes = [
17
28
  "mds-btn",
18
29
  "btn",
19
- `btn-${variant && allowedVariants.includes(variant) ? variant : "primary"}`
30
+ `btn-${resolvedVariant}`,
31
+ `mds-btn--size-${resolvedSize}`
20
32
  ];
21
- if (size) classes.push(`btn-${size}`);
33
+ if (isIconOnly) classes.push("mds-btn--icon-only");
22
34
  if (className) classes.push(className);
23
- return /* @__PURE__ */ jsxs("button", {
35
+ return /* @__PURE__ */ jsx("button", {
36
+ ref,
24
37
  className: classes.join(" "),
25
38
  type,
26
39
  ...props,
27
- children: [
28
- isIconElement(startIcon, "startIcon") ? startIcon : null,
40
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
41
+ resolvedStartIcon,
29
42
  label,
30
- isIconElement(endIcon, "endIcon") ? endIcon : null
31
- ]
43
+ resolvedStartIcon ? null : resolvedEndIcon
44
+ ] })
32
45
  });
33
- };
46
+ });
47
+ Button.displayName = "Button";
34
48
  //#endregion
35
49
  export { Button };
36
50
 
@@ -1 +1 @@
1
- {"version":3,"file":"Button.js","names":[],"sources":["../../../components/button/Button.tsx"],"sourcesContent":["import type { ButtonHTMLAttributes, ReactElement } from 'react';\nimport { isValidElement } from 'react';\n\ntype ButtonVariant =\n | 'primary'\n | 'secondary'\n | 'danger'\n | 'outline-primary'\n | 'outline-secondary'\n | 'outline-danger';\n\ntype IconElement = ReactElement<'i' | 'svg'>;\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n label?: string;\n variant?: ButtonVariant;\n size?: 'sm' | 'lg';\n startIcon?: IconElement;\n endIcon?: IconElement;\n}\n\n// Runtime guard — prop for icons must be <i> or <svg> elements\nconst isIconElement = (el: unknown, propName: string): el is IconElement => {\n const valid = isValidElement(el) && (el.type === 'i' || el.type === 'svg');\n if (!valid && el != null && import.meta.env.DEV) {\n console.error(`Button: \\`${propName}\\` must be an <i> or <svg> element.`);\n }\n return valid;\n};\n\nconst allowedVariants: ButtonVariant[] = [\n 'primary',\n 'secondary',\n 'danger',\n 'outline-primary',\n 'outline-secondary',\n 'outline-danger',\n];\n\nexport const Button = ({\n label,\n variant,\n size,\n startIcon,\n endIcon,\n className,\n type = 'button',\n ...props\n}: ButtonProps) => {\n // Warn in development if button has no accessible name\n if (import.meta.env.DEV) {\n const hasLabel = Boolean(label);\n const hasAriaLabel = 'aria-label' in props;\n if (!hasLabel && !hasAriaLabel) {\n console.warn(\n 'Button: label prop or aria-label attribute is required for accessibility.',\n );\n }\n if (variant && !allowedVariants.includes(variant as ButtonVariant)) {\n console.warn(\n `[MDS Button] Invalid variant \"${variant}\". Falling back to \"primary\". Allowed: ${allowedVariants.join(', ')}`,\n );\n }\n }\n\n const resolvedVariant =\n variant && allowedVariants.includes(variant as ButtonVariant)\n ? variant\n : 'primary';\n\n const classes = ['mds-btn', 'btn', `btn-${resolvedVariant}`];\n if (size) {\n classes.push(`btn-${size}`);\n }\n if (className) {\n classes.push(className);\n }\n\n return (\n <button className={classes.join(' ')} type={type} {...props}>\n {isIconElement(startIcon, 'startIcon') ? startIcon : null}\n {label}\n {isIconElement(endIcon, 'endIcon') ? endIcon : null}\n </button>\n );\n};\n"],"mappings":";;;AAsBA,IAAM,iBAAiB,IAAa,aAAwC;CAK1E,OAJc,eAAe,EAAE,MAAM,GAAG,SAAS,OAAO,GAAG,SAAS;AAKtE;AAEA,IAAM,kBAAmC;CACvC;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,IAAa,UAAU,EACrB,OACA,SACA,MACA,WACA,SACA,WACA,OAAO,UACP,GAAG,YACc;CAsBjB,MAAM,UAAU;EAAC;EAAW;EAAO,OAJjC,WAAW,gBAAgB,SAAS,OAAwB,IACxD,UACA;CAEqD;CAC3D,IAAI,MACF,QAAQ,KAAK,OAAO,MAAM;CAE5B,IAAI,WACF,QAAQ,KAAK,SAAS;CAGxB,OACE,qBAAC,UAAD;EAAQ,WAAW,QAAQ,KAAK,GAAG;EAAS;EAAM,GAAI;YAAtD;GACG,cAAc,WAAW,WAAW,IAAI,YAAY;GACpD;GACA,cAAc,SAAS,SAAS,IAAI,UAAU;EACzC;;AAEZ"}
1
+ {"version":3,"file":"Button.js","names":[],"sources":["../../../components/button/Button.tsx"],"sourcesContent":["import type { ButtonHTMLAttributes, ReactElement } from 'react';\nimport { forwardRef, isValidElement } from 'react';\n\ntype ButtonVariant =\n | 'primary'\n | 'secondary'\n | 'danger'\n | 'ghost'\n | 'outline-primary'\n | 'outline-secondary'\n | 'outline-danger';\n\ntype ButtonSize = 'sm' | 'md' | 'lg';\n\ntype IconElement = ReactElement<'i' | 'svg'>;\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n label?: string;\n variant?: ButtonVariant;\n size?: ButtonSize;\n startIcon?: IconElement;\n endIcon?: IconElement;\n}\n\n// Runtime guard — prop for icons must be <i> or <svg> elements\nconst isIconElement = (el: unknown, propName: string): el is IconElement => {\n const valid = isValidElement(el) && (el.type === 'i' || el.type === 'svg');\n if (!valid && el != null && import.meta.env.DEV) {\n console.error(`Button: \\`${propName}\\` must be an <i> or <svg> element.`);\n }\n return valid;\n};\n\nconst allowedVariants: ButtonVariant[] = [\n 'primary',\n 'secondary',\n 'danger',\n 'ghost',\n 'outline-primary',\n 'outline-secondary',\n 'outline-danger',\n];\n\nconst allowedSizes: ButtonSize[] = ['sm', 'md', 'lg'];\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n function Button(\n {\n label,\n variant,\n size,\n startIcon,\n endIcon,\n className,\n type = 'button',\n ...props\n },\n ref,\n ) {\n const resolvedVariant =\n variant && allowedVariants.includes(variant as ButtonVariant)\n ? variant\n : 'primary';\n const resolvedSize =\n size && allowedSizes.includes(size as ButtonSize) ? size : 'md';\n const resolvedStartIcon = isIconElement(startIcon, 'startIcon')\n ? startIcon\n : null;\n const resolvedEndIcon = isIconElement(endIcon, 'endIcon') ? endIcon : null;\n\n if (import.meta.env.DEV) {\n const hasAccessibleName =\n Boolean(label) ||\n Boolean(props['aria-label']?.trim()) ||\n Boolean(props['aria-labelledby']?.trim());\n if (!hasAccessibleName) {\n console.warn(\n 'Button: provide a label, aria-label, or aria-labelledby for accessibility.',\n );\n }\n if (variant && !allowedVariants.includes(variant as ButtonVariant)) {\n console.warn(\n `[MDS Button] Invalid variant \"${variant}\". Falling back to \"primary\". Allowed: ${allowedVariants.join(', ')}`,\n );\n }\n if (size && !allowedSizes.includes(size as ButtonSize)) {\n console.warn(\n `[MDS Button] Invalid size \"${size}\". Falling back to \"md\". Allowed: ${allowedSizes.join(', ')}`,\n );\n }\n if (resolvedStartIcon && resolvedEndIcon) {\n console.warn(\n 'Button: pass either startIcon or endIcon, not both. Rendering startIcon only.',\n );\n }\n if (!label && !resolvedStartIcon && !resolvedEndIcon) {\n console.warn(\n 'Button: provide a label or icon so the button does not render as visually empty.',\n );\n }\n }\n\n const isIconOnly = !label && Boolean(resolvedStartIcon || resolvedEndIcon);\n\n const classes = [\n 'mds-btn',\n 'btn',\n `btn-${resolvedVariant}`,\n `mds-btn--size-${resolvedSize}`,\n ];\n if (isIconOnly) {\n classes.push('mds-btn--icon-only');\n }\n if (className) {\n classes.push(className);\n }\n\n return (\n <button ref={ref} className={classes.join(' ')} type={type} {...props}>\n <>\n {resolvedStartIcon}\n {label}\n {resolvedStartIcon ? null : resolvedEndIcon}\n </>\n </button>\n );\n },\n);\nButton.displayName = 'Button';\n"],"mappings":";;;AAyBA,IAAM,iBAAiB,IAAa,aAAwC;CAK1E,OAJc,eAAe,EAAE,MAAM,GAAG,SAAS,OAAO,GAAG,SAAS;AAKtE;AAEA,IAAM,kBAAmC;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,IAAM,eAA6B;CAAC;CAAM;CAAM;AAAI;AAEpD,IAAa,SAAS,WACpB,SAAS,OACP,EACE,OACA,SACA,MACA,WACA,SACA,WACA,OAAO,UACP,GAAG,SAEL,KACA;CACA,MAAM,kBACJ,WAAW,gBAAgB,SAAS,OAAwB,IACxD,UACA;CACN,MAAM,eACJ,QAAQ,aAAa,SAAS,IAAkB,IAAI,OAAO;CAC7D,MAAM,oBAAoB,cAAc,WAAW,WAAW,IAC1D,YACA;CACJ,MAAM,kBAAkB,cAAc,SAAS,SAAS,IAAI,UAAU;CAkCtE,MAAM,aAAa,CAAC,SAAS,QAAQ,qBAAqB,eAAe;CAEzE,MAAM,UAAU;EACd;EACA;EACA,OAAO;EACP,iBAAiB;CACnB;CACA,IAAI,YACF,QAAQ,KAAK,oBAAoB;CAEnC,IAAI,WACF,QAAQ,KAAK,SAAS;CAGxB,OACE,oBAAC,UAAD;EAAa;EAAK,WAAW,QAAQ,KAAK,GAAG;EAAS;EAAM,GAAI;YAC9D,qBAAA,UAAA,EAAA,UAAA;GACG;GACA;GACA,oBAAoB,OAAO;EAC5B,EAAA,CAAA;CACI,CAAA;AAEZ,CACF;AACA,OAAO,cAAc"}
@@ -0,0 +1,23 @@
1
+ import { InputHTMLAttributes } from 'react';
2
+ export interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
3
+ /** Visible label text. When hideLabel is true this also serves as the aria-label fallback
4
+ * if no explicit aria-label prop is provided. */
5
+ label?: string;
6
+ /** When true, the visible label element is hidden. The input is still labelled accessibly
7
+ * via aria-label (prop) → label (prop) in that order of precedence. Suppresses
8
+ * invalidFeedback — feedback text requires a visible label to provide context. */
9
+ hideLabel?: boolean;
10
+ /** Marks the input as invalid: applies danger border/label colour and sets aria-invalid.
11
+ * Independent of invalidFeedback — invalid styling can be shown without a message. */
12
+ invalid?: boolean;
13
+ /** Renders the checkbox in a mixed state, typically for "select all" parent controls.
14
+ * This state is visual/semantic and should usually be controlled by parent logic. */
15
+ indeterminate?: boolean;
16
+ /** Optional supporting/helper text shown below the label in non-error state.
17
+ * Hidden when hideLabel is true. */
18
+ supportingText?: string;
19
+ /** Pre-translated error message rendered below the label. Requires invalid={true} and
20
+ * hideLabel={false} to be displayed. */
21
+ invalidFeedback?: string;
22
+ }
23
+ export declare const Checkbox: import('react').ForwardRefExoticComponent<CheckboxProps & import('react').RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,68 @@
1
+ import { forwardRef, useEffect, useId, useRef } from "react";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region components/checkbox/Checkbox.tsx
4
+ var Checkbox = forwardRef(({ invalidFeedback, invalid, indeterminate = false, supportingText, className, label, hideLabel = false, id: idProp, required, "aria-label": ariaLabelProp, ...inputProps }, ref) => {
5
+ const generatedId = useId();
6
+ const id = idProp ?? generatedId;
7
+ const hasVisibleLabel = !hideLabel;
8
+ const isInvalid = !!invalid;
9
+ const isIndeterminate = !!indeterminate;
10
+ const inputRef = useRef(null);
11
+ useEffect(() => {
12
+ if (inputRef.current) inputRef.current.indeterminate = isIndeterminate;
13
+ }, [isIndeterminate]);
14
+ const classes = ["mds-checkbox"];
15
+ if (hasVisibleLabel) classes.push("form-check");
16
+ if (className) classes.push(className);
17
+ const ariaLabel = hideLabel ? ariaLabelProp ?? label : void 0;
18
+ const messageText = hasVisibleLabel ? isInvalid && invalidFeedback ? invalidFeedback : supportingText : void 0;
19
+ const hasInvalidFeedback = hasVisibleLabel && isInvalid && !!invalidFeedback;
20
+ const feedbackId = messageText ? `${id}-feedback` : void 0;
21
+ return /* @__PURE__ */ jsxs("div", {
22
+ className: classes.join(" "),
23
+ children: [
24
+ /* @__PURE__ */ jsx("input", {
25
+ className: [
26
+ "mds-checkbox-input",
27
+ "form-check-input",
28
+ isInvalid ? "is-invalid" : ""
29
+ ].filter(Boolean).join(" "),
30
+ ref: (node) => {
31
+ inputRef.current = node;
32
+ if (typeof ref === "function") ref(node);
33
+ else if (ref) ref.current = node;
34
+ },
35
+ ...inputProps,
36
+ type: "checkbox",
37
+ required,
38
+ "aria-invalid": isInvalid ? true : void 0,
39
+ "aria-label": ariaLabel,
40
+ "aria-checked": isIndeterminate ? "mixed" : void 0,
41
+ "aria-describedby": feedbackId,
42
+ id
43
+ }),
44
+ hasVisibleLabel && /* @__PURE__ */ jsxs("label", {
45
+ className: "mds-checkbox-label form-check-label",
46
+ htmlFor: id,
47
+ children: [/* @__PURE__ */ jsx("span", {
48
+ className: "mds-checkbox-label-text",
49
+ children: label
50
+ }), required && /* @__PURE__ */ jsx("span", {
51
+ className: "mds-checkbox-required",
52
+ "aria-hidden": "true",
53
+ children: "*"
54
+ })]
55
+ }),
56
+ feedbackId && /* @__PURE__ */ jsx("div", {
57
+ id: feedbackId,
58
+ className: ["mds-checkbox-feedback", hasInvalidFeedback ? "invalid-feedback" : "mds-checkbox-supporting-text"].filter(Boolean).join(" "),
59
+ children: messageText
60
+ })
61
+ ]
62
+ });
63
+ });
64
+ Checkbox.displayName = "Checkbox";
65
+ //#endregion
66
+ export { Checkbox };
67
+
68
+ //# sourceMappingURL=Checkbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkbox.js","names":[],"sources":["../../../components/checkbox/Checkbox.tsx"],"sourcesContent":["import {\n type InputHTMLAttributes,\n forwardRef,\n useEffect,\n useId,\n useRef,\n} from 'react';\n\nexport interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {\n /** Visible label text. When hideLabel is true this also serves as the aria-label fallback\n * if no explicit aria-label prop is provided. */\n label?: string;\n /** When true, the visible label element is hidden. The input is still labelled accessibly\n * via aria-label (prop) → label (prop) in that order of precedence. Suppresses\n * invalidFeedback — feedback text requires a visible label to provide context. */\n hideLabel?: boolean;\n /** Marks the input as invalid: applies danger border/label colour and sets aria-invalid.\n * Independent of invalidFeedback — invalid styling can be shown without a message. */\n invalid?: boolean;\n /** Renders the checkbox in a mixed state, typically for \"select all\" parent controls.\n * This state is visual/semantic and should usually be controlled by parent logic. */\n indeterminate?: boolean;\n /** Optional supporting/helper text shown below the label in non-error state.\n * Hidden when hideLabel is true. */\n supportingText?: string;\n /** Pre-translated error message rendered below the label. Requires invalid={true} and\n * hideLabel={false} to be displayed. */\n invalidFeedback?: string;\n}\n\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(\n (\n {\n invalidFeedback,\n invalid,\n indeterminate = false,\n supportingText,\n className,\n label,\n hideLabel = false,\n id: idProp,\n required,\n 'aria-label': ariaLabelProp,\n ...inputProps\n }: CheckboxProps,\n ref,\n ) => {\n const generatedId = useId();\n const id = idProp ?? generatedId;\n const hasVisibleLabel = !hideLabel;\n const isInvalid = !!invalid;\n const isIndeterminate = !!indeterminate;\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n useEffect(() => {\n if (inputRef.current) inputRef.current.indeterminate = isIndeterminate;\n }, [isIndeterminate]);\n\n const warnDev = (condition: boolean, message: string) => {\n if (import.meta.env.DEV && condition) {\n console.warn(message);\n }\n };\n\n if (import.meta.env.DEV) {\n warnDev(\n hideLabel && !ariaLabelProp && !label,\n 'Checkbox: label prop or aria-label attribute is required for accessibility when hideLabel is true.',\n );\n warnDev(\n !hideLabel && !label,\n 'Checkbox: label prop is required when hideLabel is false. An empty label creates an inaccessible form control.',\n );\n warnDev(\n hideLabel && !!invalidFeedback,\n 'Checkbox: invalidFeedback is ignored when hideLabel is true. Feedback text requires a visible label to provide context.',\n );\n warnDev(\n !hideLabel && !!invalidFeedback && !invalid,\n 'Checkbox: invalidFeedback is provided without invalid={true}. Pass invalid={true} to apply invalid styling alongside the feedback text.',\n );\n }\n\n const classes = ['mds-checkbox'];\n if (hasVisibleLabel) classes.push('form-check');\n if (className) {\n classes.push(className);\n }\n\n const ariaLabel = hideLabel ? (ariaLabelProp ?? label) : undefined;\n const messageText = hasVisibleLabel\n ? isInvalid && invalidFeedback\n ? invalidFeedback\n : supportingText\n : undefined;\n const hasInvalidFeedback =\n hasVisibleLabel && isInvalid && !!invalidFeedback;\n const feedbackId = messageText ? `${id}-feedback` : undefined;\n\n return (\n <div className={classes.join(' ')}>\n <input\n className={[\n 'mds-checkbox-input',\n 'form-check-input',\n isInvalid ? 'is-invalid' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n ref={(node) => {\n // Keep a local ref for the native indeterminate property while still forwarding refs.\n inputRef.current = node;\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n }}\n {...inputProps}\n type=\"checkbox\"\n required={required}\n aria-invalid={isInvalid ? true : undefined}\n aria-label={ariaLabel}\n aria-checked={isIndeterminate ? 'mixed' : undefined}\n aria-describedby={feedbackId}\n id={id}\n />\n {hasVisibleLabel && (\n <label className=\"mds-checkbox-label form-check-label\" htmlFor={id}>\n <span className=\"mds-checkbox-label-text\">{label}</span>\n {required && (\n <span className=\"mds-checkbox-required\" aria-hidden=\"true\">\n *\n </span>\n )}\n </label>\n )}\n {feedbackId && (\n <div\n id={feedbackId}\n className={[\n 'mds-checkbox-feedback',\n hasInvalidFeedback\n ? 'invalid-feedback'\n : 'mds-checkbox-supporting-text',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {messageText}\n </div>\n )}\n </div>\n );\n },\n);\nCheckbox.displayName = 'Checkbox';\n"],"mappings":";;;AA8BA,IAAa,WAAW,YAEpB,EACE,iBACA,SACA,gBAAgB,OAChB,gBACA,WACA,OACA,YAAY,OACZ,IAAI,QACJ,UACA,cAAc,eACd,GAAG,cAEL,QACG;CACH,MAAM,cAAc,MAAM;CAC1B,MAAM,KAAK,UAAU;CACrB,MAAM,kBAAkB,CAAC;CACzB,MAAM,YAAY,CAAC,CAAC;CACpB,MAAM,kBAAkB,CAAC,CAAC;CAC1B,MAAM,WAAW,OAAgC,IAAI;CAErD,gBAAgB;EACd,IAAI,SAAS,SAAS,SAAS,QAAQ,gBAAgB;CACzD,GAAG,CAAC,eAAe,CAAC;CA2BpB,MAAM,UAAU,CAAC,cAAc;CAC/B,IAAI,iBAAiB,QAAQ,KAAK,YAAY;CAC9C,IAAI,WACF,QAAQ,KAAK,SAAS;CAGxB,MAAM,YAAY,YAAa,iBAAiB,QAAS,KAAA;CACzD,MAAM,cAAc,kBAChB,aAAa,kBACX,kBACA,iBACF,KAAA;CACJ,MAAM,qBACJ,mBAAmB,aAAa,CAAC,CAAC;CACpC,MAAM,aAAa,cAAc,GAAG,GAAG,aAAa,KAAA;CAEpD,OACE,qBAAC,OAAD;EAAK,WAAW,QAAQ,KAAK,GAAG;YAAhC;GACE,oBAAC,SAAD;IACE,WAAW;KACT;KACA;KACA,YAAY,eAAe;IAC7B,EACG,OAAO,OAAO,EACd,KAAK,GAAG;IACX,MAAM,SAAS;KAEb,SAAS,UAAU;KACnB,IAAI,OAAO,QAAQ,YACjB,IAAI,IAAI;UACH,IAAI,KACT,IAAI,UAAU;IAElB;IACA,GAAI;IACJ,MAAK;IACK;IACV,gBAAc,YAAY,OAAO,KAAA;IACjC,cAAY;IACZ,gBAAc,kBAAkB,UAAU,KAAA;IAC1C,oBAAkB;IACd;GACL,CAAA;GACA,mBACC,qBAAC,SAAD;IAAO,WAAU;IAAsC,SAAS;cAAhE,CACE,oBAAC,QAAD;KAAM,WAAU;eAA2B;IAAY,CAAA,GACtD,YACC,oBAAC,QAAD;KAAM,WAAU;KAAwB,eAAY;eAAO;IAErD,CAAA,CAEH;;GAER,cACC,oBAAC,OAAD;IACE,IAAI;IACJ,WAAW,CACT,yBACA,qBACI,qBACA,8BACN,EACG,OAAO,OAAO,EACd,KAAK,GAAG;cAEV;GACE,CAAA;EAEJ;;AAET,CACF;AACA,SAAS,cAAc"}
@@ -0,0 +1 @@
1
+ export * from './Checkbox';
@@ -0,0 +1,2 @@
1
+ import { Checkbox } from "./Checkbox.js";
2
+ export { Checkbox };
@@ -1,7 +1,11 @@
1
1
  export { ActivityIcon } from './activity-icon';
2
2
  export type { ActivityIconProps } from './activity-icon';
3
+ export { Badge } from './badge';
4
+ export type { BadgeProps } from './badge';
3
5
  export { Button } from './button';
4
6
  export type { ButtonProps } from './button';
7
+ export { Checkbox } from './checkbox';
8
+ export type { CheckboxProps } from './checkbox';
5
9
  export { CloseButton } from './close-button';
6
10
  export type { CloseButtonProps } from './close-button';
7
11
  export { Radio } from './radio';