@gnist/design-system 3.5.6 → 3.7.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.7.0](https://github.com/mollerdigital/design-system-design-system/compare/@gnist/design-system@3.6.0...@gnist/design-system@3.7.0) (2025-07-11)
7
+
8
+ ### Features
9
+
10
+ * add mode prop to TextField ([015a59b](https://github.com/mollerdigital/design-system-design-system/commit/015a59b522fc04d0526a67de9d0e2ecc0fd3396f))
11
+
12
+ ## [3.6.0](https://github.com/mollerdigital/design-system-design-system/compare/@gnist/design-system@3.5.6...@gnist/design-system@3.6.0) (2025-07-07)
13
+
14
+ ### Features
15
+
16
+ * AlertBanner message-prop allows React Node ([c911154](https://github.com/mollerdigital/design-system-design-system/commit/c911154f4a3c2b8bea410bf963a9c4b14943a508))
17
+
6
18
  ## [3.5.6](https://github.com/mollerdigital/design-system-design-system/compare/@gnist/design-system@3.5.5...@gnist/design-system@3.5.6) (2025-07-04)
7
19
 
8
20
  ### Bug Fixes
@@ -23,6 +23,6 @@ const classNames__default = /* @__PURE__ */ _interopDefaultCompat(classNames);
23
23
  const BannerHeading = componentUtils.component("BannerHeading", AlertBanner_css.bannerHeading, "h2");
24
24
  const AlertBanner = ({ type, heading, message, dismiss, action, density = "default", headingLevel = "h2", className }) => {
25
25
  const text = index.useTranslation((t) => t.components.feedback.alerts);
26
- return jsxRuntime.jsxs("div", { className: classNames__default.default(className, AlertBanner_css.banner({ type, density })), children: [jsxRuntime.jsx(Icon.Icon, { type, icon: type, className: AlertBanner_css.icon({ type }) }), jsxRuntime.jsxs("div", { className: AlertBanner_css.mainContentContainer({ density }), children: [heading && jsxRuntime.jsx(BannerHeading, { "$as": headingLevel, children: heading }), dismiss && jsxRuntime.jsx(IconButton.IconButton, { className: AlertBanner_css.closeButton, icon: "close", label: text.dismissLabel, onClick: dismiss }), jsxRuntime.jsx(index$1.TextContainer, { className: AlertBanner_css.messageContainer, children: message }), action && jsxRuntime.jsx(TextButton.TextButton, { density: "compact", className: AlertBanner_css.actionButton, onClick: action.onClick, underline: true, children: action.label })] })] });
26
+ return jsxRuntime.jsxs("div", { className: classNames__default.default(className, AlertBanner_css.banner({ type, density })), children: [jsxRuntime.jsx(Icon.Icon, { type, icon: type, className: AlertBanner_css.icon({ type }) }), jsxRuntime.jsxs("div", { className: AlertBanner_css.mainContentContainer({ density }), children: [heading && jsxRuntime.jsx(BannerHeading, { "$as": headingLevel, children: heading }), dismiss && jsxRuntime.jsx(IconButton.IconButton, { className: AlertBanner_css.closeButton, icon: "close", label: text.dismissLabel, onClick: dismiss }), typeof message === "string" ? jsxRuntime.jsx(index$1.TextContainer, { className: AlertBanner_css.messageContainer, children: message }) : jsxRuntime.jsx("div", { className: AlertBanner_css.messageContainer, children: message }), action && jsxRuntime.jsx(TextButton.TextButton, { density: "compact", className: AlertBanner_css.actionButton, onClick: action.onClick, underline: true, children: action.label })] })] });
27
27
  };
28
28
  exports.AlertBanner = AlertBanner;
@@ -1,8 +1,9 @@
1
1
  import { DensitySizes } from "../../../foundation/typography";
2
+ import { ReactNode } from "react";
2
3
  export interface AlertBannerProps {
3
4
  type: "info" | "success" | "warning" | "error";
4
5
  heading?: string | undefined;
5
- message: string;
6
+ message: ReactNode;
6
7
  dismiss?: () => void;
7
8
  action?: {
8
9
  label: string;
@@ -14,7 +15,7 @@ export interface AlertBannerProps {
14
15
  }
15
16
  /** An alert banner displays an important and high-signal message, and provides actions for users to address (or dismiss the banner). They’re meant to be noticed and often requires a user action to be dismissed.
16
17
 
17
- Text is required for all alert banners. The message should be concise and, if applicable, describe the next step that a user can take.
18
+ Message is required for all alert banners. The message should be concise and, if applicable, describe the next step that a user can take. The message prop can be string or other React Node.
18
19
 
19
20
  Title and button with a contextual action are optional.
20
21
 
@@ -1 +1 @@
1
- {"version":3,"file":"AlertBanner.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/alerts/AlertBanner.tsx"],"names":[],"mappings":"AAEA,OAAO,EACH,YAAY,EAEf,uCAAmD;AAcpD,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,IAAI,CAAC;KACvB,CAAC;IACF,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAID;;;;;;;GAOG;AAEH,eAAO,MAAM,WAAW,mFASrB,gBAAgB,4CAmClB,CAAC"}
1
+ {"version":3,"file":"AlertBanner.d.ts","sourceRoot":"","sources":["../../../../src/components/feedback/alerts/AlertBanner.tsx"],"names":[],"mappings":"AAEA,OAAO,EACH,YAAY,EAEf,uCAAmD;AAapD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,IAAI,CAAC;KACvB,CAAC;IACF,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAID;;;;;;;GAOG;AAEH,eAAO,MAAM,WAAW,mFASrB,gBAAgB,4CAuClB,CAAC"}
@@ -19,7 +19,7 @@ import { bannerHeading, icon, mainContentContainer, closeButton, messageContaine
19
19
  const BannerHeading = component("BannerHeading", bannerHeading, "h2");
20
20
  const AlertBanner = ({ type, heading, message, dismiss, action, density = "default", headingLevel = "h2", className }) => {
21
21
  const text = useTranslation((t) => t.components.feedback.alerts);
22
- return jsxs("div", { className: classNames(className, banner({ type, density })), children: [jsx(Icon, { type, icon: type, className: icon({ type }) }), jsxs("div", { className: mainContentContainer({ density }), children: [heading && jsx(BannerHeading, { "$as": headingLevel, children: heading }), dismiss && jsx(IconButton, { className: closeButton, icon: "close", label: text.dismissLabel, onClick: dismiss }), jsx(TextContainer, { className: messageContainer, children: message }), action && jsx(TextButton, { density: "compact", className: actionButton, onClick: action.onClick, underline: true, children: action.label })] })] });
22
+ return jsxs("div", { className: classNames(className, banner({ type, density })), children: [jsx(Icon, { type, icon: type, className: icon({ type }) }), jsxs("div", { className: mainContentContainer({ density }), children: [heading && jsx(BannerHeading, { "$as": headingLevel, children: heading }), dismiss && jsx(IconButton, { className: closeButton, icon: "close", label: text.dismissLabel, onClick: dismiss }), typeof message === "string" ? jsx(TextContainer, { className: messageContainer, children: message }) : jsx("div", { className: messageContainer, children: message }), action && jsx(TextButton, { density: "compact", className: actionButton, onClick: action.onClick, underline: true, children: action.label })] })] });
23
23
  };
24
24
  export {
25
25
  AlertBanner
@@ -23,17 +23,40 @@ const shared = require("../shared.cjs");
23
23
  const _interopDefaultCompat = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
24
24
  const classNames__default = /* @__PURE__ */ _interopDefaultCompat(classNames);
25
25
  const TextField = React.forwardRef(function TextField2(props, ref) {
26
- var _a, _b;
26
+ var _a, _b, _c, _d;
27
27
  const { wrapperProps, contentProps, commonInputProps } = shared.useInputFieldLogic(props);
28
28
  const { inputProps } = shared.getInputFieldProps(props);
29
+ const isModeCurrencyOrNumberFormatting = ((_a = props.mode) == null ? void 0 : _a.style) === "currency" || ((_b = props.mode) == null ? void 0 : _b.style) === "numberSpacing";
30
+ const onChangeHandler = (event) => {
31
+ var _a2;
32
+ if (isModeCurrencyOrNumberFormatting) {
33
+ event.target.value = event.target.value.replace(new RegExp("(?<!^)-|[^\\d\\W.,]+|[.,]|\\s+", "g"), "");
34
+ }
35
+ (_a2 = inputProps.onChange) == null ? void 0 : _a2.call(inputProps, event);
36
+ };
37
+ if (isModeCurrencyOrNumberFormatting && !!inputProps.value) {
38
+ inputProps.type = "text";
39
+ inputProps.inputMode = "numeric";
40
+ const number = inputProps.value;
41
+ const isNegativeNumber = number[0] === "-";
42
+ inputProps.value = isNegativeNumber ? inputProps.value.slice(1) : inputProps.value;
43
+ const numberPrefix = isNegativeNumber ? "-" : "";
44
+ const formattedValue = new Intl.NumberFormat("nb-NB", {
45
+ style: "currency",
46
+ currency: "NOK",
47
+ minimumFractionDigits: 0,
48
+ maximumFractionDigits: 5
49
+ }).format(parseFloat(inputProps.value)).replaceAll(/[^0-9.,\W]/g, "").trim();
50
+ inputProps.value = numberPrefix + formattedValue;
51
+ }
29
52
  return jsxRuntime.jsxs("span", { ref, className: classNames__default.default(wrapperProps.className, inputField_css.wrapperStyle), children: [props.leadingIcon && shared.isClickableIcon(props.leadingIcon) && jsxRuntime.jsx("button", { className: iconButtonOverlay_css.iconButtonOverlayRecipe({
30
53
  placement: "left",
31
54
  density: wrapperProps.density
32
55
  }), title: props.leadingIcon.title, onClick: props.leadingIcon.onClick }), jsxRuntime.jsxs("label", { htmlFor: props.id, className: inputField_css.inputFieldWrapperRecipe({
33
56
  disabled: props.disabled,
34
57
  density: props.density,
35
- validityType: (_a = props.validity) == null ? void 0 : _a.type
36
- }), children: [jsxRuntime.jsx("input", { ...commonInputProps, ...inputProps, className: classNames__default.default(inputField_css.inputFieldStyle, inputProps.className), style: dynamic.assignInlineVars({
58
+ validityType: (_c = props.validity) == null ? void 0 : _c.type
59
+ }), children: [jsxRuntime.jsx("input", { ...commonInputProps, ...inputProps, value: inputProps.value, onChange: onChangeHandler, className: classNames__default.default(inputField_css.inputFieldStyle, inputProps.className), style: dynamic.assignInlineVars({
37
60
  [inputFieldConstants_css.preInputWidth]: wrapperProps.preInputWidth,
38
61
  [inputFieldConstants_css.postInputWidth]: wrapperProps.postInputWidth
39
62
  }), onFocus: (event) => {
@@ -44,7 +67,7 @@ const TextField = React.forwardRef(function TextField2(props, ref) {
44
67
  (_b2 = inputProps.onFocus) == null ? void 0 : _b2.call(inputProps, event);
45
68
  } }), jsxRuntime.jsxs("span", { className: inputField_css.inputContentWrapper({ density: props.density }), children: [props.leadingIcon && jsxRuntime.jsx(Icon.Icon, { icon: shared.isClickableIcon(props.leadingIcon) ? props.leadingIcon.icon : props.leadingIcon, className: atoms_css_js.atoms({ paddingRight: "xxs" }) }), props.prefix && jsxRuntime.jsx("span", { ref: contentProps.prefixRef, "aria-hidden": true, className: suffixPrefix_css.prefixStyle, children: props.prefix }), jsxRuntime.jsx("label", { id: contentProps.labelId, className: labelStyles_css.labelStyle({
46
69
  density: props.density,
47
- validityType: (_b = props.validity) == null ? void 0 : _b.type,
70
+ validityType: (_d = props.validity) == null ? void 0 : _d.type,
48
71
  isElevated: !!props.value || props.type === "date" || props.type === "time",
49
72
  disabled: props.disabled
50
73
  }), style: {
@@ -1,12 +1,17 @@
1
1
  import { InputHTMLAttributes } from "react";
2
2
  import { TextInputProps } from "../shared.js";
3
- export type TextFieldProps = TextInputProps & Omit<InputHTMLAttributes<HTMLInputElement>, "placeholder" | "value" | "type"> & {
3
+ export type TextFieldModeProps = {
4
4
  /**
5
- * Input type
6
- * @default text
5
+ * Applies number spacing and numeric input mode
7
6
  */
8
- type?: "email" | "search" | "tel" | "text" | "url" | "password" | "number" | "date" | "time";
7
+ mode?: {
8
+ style: "numberSpacing";
9
+ } | {
10
+ style: "currency";
11
+ currency: "NOK";
12
+ };
9
13
  };
14
+ export type TextFieldProps = TextInputProps & TextFieldModeProps & Omit<InputHTMLAttributes<HTMLInputElement>, "placeholder" | "value">;
10
15
  /**
11
16
  A text field is an input that allows a user to write or edit text. Text fields typically appear in forms and dialogs.
12
17
 
@@ -18,11 +23,5 @@ _Accessibility note:_ The leading/trailing icon and prefix/suffix text will not
18
23
 
19
24
  Documentation: [TextField](https://gnist.moller.no/developers/components/latest/?path=/docs/components-inputs-textfields-textfield--docs)
20
25
  */
21
- export declare const TextField: import("react").ForwardRefExoticComponent<TextInputProps & Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "type" | "placeholder"> & {
22
- /**
23
- * Input type
24
- * @default text
25
- */
26
- type?: "email" | "search" | "tel" | "text" | "url" | "password" | "number" | "date" | "time";
27
- } & import("react").RefAttributes<HTMLSpanElement>>;
26
+ export declare const TextField: import("react").ForwardRefExoticComponent<TextInputProps & TextFieldModeProps & Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "placeholder"> & import("react").RefAttributes<HTMLSpanElement>>;
28
27
  //# sourceMappingURL=TextField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/inputs/textFields/TextField.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA4B,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAetE,OAAO,EAGH,cAAc,EAEjB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,cAAc,GAAG,cAAc,GACvC,IAAI,CACA,mBAAmB,CAAC,gBAAgB,CAAC,EACrC,aAAa,GAAG,OAAO,GAAG,MAAM,CACnC,GAAG;IACA;;;OAGG;IACH,IAAI,CAAC,EACC,OAAO,GACP,QAAQ,GACR,KAAK,GACL,MAAM,GACN,KAAK,GACL,UAAU,GACV,QAAQ,GACR,MAAM,GACN,MAAM,CAAC;CAChB,CAAC;AAEN;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS;IA3Bd;;;OAGG;WAEG,OAAO,GACP,QAAQ,GACR,KAAK,GACL,MAAM,GACN,KAAK,GACL,UAAU,GACV,QAAQ,GACR,MAAM,GACN,MAAM;mDAuJlB,CAAC"}
1
+ {"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/inputs/textFields/TextField.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA4B,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAetE,OAAO,EAGH,cAAc,EAEjB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,kBAAkB,GAAG;IAC7B;;OAEG;IACH,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,eAAe,CAAA;KAAE,GAAG;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAA;KAAE,CAAC;CAC9E,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,cAAc,GACvC,kBAAkB,GAClB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,wMAqLpB,CAAC"}
@@ -19,17 +19,40 @@ import { labelStyle } from "../shared-styles/labelStyles.css.js";
19
19
  import { prefixStyle, suffixStyle } from "../shared-styles/suffixPrefix.css.js";
20
20
  import { useInputFieldLogic, getInputFieldProps, isClickableIcon } from "../shared.js";
21
21
  const TextField = forwardRef(function TextField2(props, ref) {
22
- var _a, _b;
22
+ var _a, _b, _c, _d;
23
23
  const { wrapperProps, contentProps, commonInputProps } = useInputFieldLogic(props);
24
24
  const { inputProps } = getInputFieldProps(props);
25
+ const isModeCurrencyOrNumberFormatting = ((_a = props.mode) == null ? void 0 : _a.style) === "currency" || ((_b = props.mode) == null ? void 0 : _b.style) === "numberSpacing";
26
+ const onChangeHandler = (event) => {
27
+ var _a2;
28
+ if (isModeCurrencyOrNumberFormatting) {
29
+ event.target.value = event.target.value.replace(new RegExp("(?<!^)-|[^\\d\\W.,]+|[.,]|\\s+", "g"), "");
30
+ }
31
+ (_a2 = inputProps.onChange) == null ? void 0 : _a2.call(inputProps, event);
32
+ };
33
+ if (isModeCurrencyOrNumberFormatting && !!inputProps.value) {
34
+ inputProps.type = "text";
35
+ inputProps.inputMode = "numeric";
36
+ const number = inputProps.value;
37
+ const isNegativeNumber = number[0] === "-";
38
+ inputProps.value = isNegativeNumber ? inputProps.value.slice(1) : inputProps.value;
39
+ const numberPrefix = isNegativeNumber ? "-" : "";
40
+ const formattedValue = new Intl.NumberFormat("nb-NB", {
41
+ style: "currency",
42
+ currency: "NOK",
43
+ minimumFractionDigits: 0,
44
+ maximumFractionDigits: 5
45
+ }).format(parseFloat(inputProps.value)).replaceAll(/[^0-9.,\W]/g, "").trim();
46
+ inputProps.value = numberPrefix + formattedValue;
47
+ }
25
48
  return jsxs("span", { ref, className: classNames(wrapperProps.className, wrapperStyle), children: [props.leadingIcon && isClickableIcon(props.leadingIcon) && jsx("button", { className: iconButtonOverlayRecipe({
26
49
  placement: "left",
27
50
  density: wrapperProps.density
28
51
  }), title: props.leadingIcon.title, onClick: props.leadingIcon.onClick }), jsxs("label", { htmlFor: props.id, className: inputFieldWrapperRecipe({
29
52
  disabled: props.disabled,
30
53
  density: props.density,
31
- validityType: (_a = props.validity) == null ? void 0 : _a.type
32
- }), children: [jsx("input", { ...commonInputProps, ...inputProps, className: classNames(inputFieldStyle, inputProps.className), style: assignInlineVars({
54
+ validityType: (_c = props.validity) == null ? void 0 : _c.type
55
+ }), children: [jsx("input", { ...commonInputProps, ...inputProps, value: inputProps.value, onChange: onChangeHandler, className: classNames(inputFieldStyle, inputProps.className), style: assignInlineVars({
33
56
  [preInputWidth]: wrapperProps.preInputWidth,
34
57
  [postInputWidth]: wrapperProps.postInputWidth
35
58
  }), onFocus: (event) => {
@@ -40,7 +63,7 @@ const TextField = forwardRef(function TextField2(props, ref) {
40
63
  (_b2 = inputProps.onFocus) == null ? void 0 : _b2.call(inputProps, event);
41
64
  } }), jsxs("span", { className: inputContentWrapper({ density: props.density }), children: [props.leadingIcon && jsx(Icon, { icon: isClickableIcon(props.leadingIcon) ? props.leadingIcon.icon : props.leadingIcon, className: atoms({ paddingRight: "xxs" }) }), props.prefix && jsx("span", { ref: contentProps.prefixRef, "aria-hidden": true, className: prefixStyle, children: props.prefix }), jsx("label", { id: contentProps.labelId, className: labelStyle({
42
65
  density: props.density,
43
- validityType: (_b = props.validity) == null ? void 0 : _b.type,
66
+ validityType: (_d = props.validity) == null ? void 0 : _d.type,
44
67
  isElevated: !!props.value || props.type === "date" || props.type === "time",
45
68
  disabled: props.disabled
46
69
  }), style: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnist/design-system",
3
- "version": "3.5.6",
3
+ "version": "3.7.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -102,5 +102,5 @@
102
102
  "optional": true
103
103
  }
104
104
  },
105
- "gitHead": "78272447930d8da86b147c877ad67c28fdfe6d50"
105
+ "gitHead": "14c41444b303c99e0b9eca32b48848f84a07213c"
106
106
  }