@canonical/react-components 4.2.0 → 4.3.1

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.
Files changed (27) hide show
  1. package/dist/components/OutputField/OutputField.d.ts +30 -0
  2. package/dist/components/OutputField/OutputField.js +44 -0
  3. package/dist/components/OutputField/OutputField.scss +11 -0
  4. package/dist/components/OutputField/OutputField.stories.d.ts +8 -0
  5. package/dist/components/OutputField/OutputField.stories.js +62 -0
  6. package/dist/components/OutputField/OutputField.test.d.ts +1 -0
  7. package/dist/components/OutputField/index.d.ts +2 -0
  8. package/dist/components/OutputField/index.js +13 -0
  9. package/dist/components/PrefixedIpInput/PrefixedIpInput.js +1 -1
  10. package/dist/components/PrefixedIpInput/utils.d.ts +10 -1
  11. package/dist/components/PrefixedIpInput/utils.js +43 -11
  12. package/dist/esm/components/OutputField/OutputField.d.ts +30 -0
  13. package/dist/esm/components/OutputField/OutputField.js +36 -0
  14. package/dist/esm/components/OutputField/OutputField.scss +11 -0
  15. package/dist/esm/components/OutputField/OutputField.stories.d.ts +8 -0
  16. package/dist/esm/components/OutputField/OutputField.stories.js +55 -0
  17. package/dist/esm/components/OutputField/OutputField.test.d.ts +1 -0
  18. package/dist/esm/components/OutputField/index.d.ts +2 -0
  19. package/dist/esm/components/OutputField/index.js +1 -0
  20. package/dist/esm/components/PrefixedIpInput/PrefixedIpInput.js +1 -1
  21. package/dist/esm/components/PrefixedIpInput/utils.d.ts +10 -1
  22. package/dist/esm/components/PrefixedIpInput/utils.js +38 -8
  23. package/dist/esm/index.d.ts +2 -0
  24. package/dist/esm/index.js +1 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +8 -0
  27. package/package.json +2 -2
@@ -0,0 +1,30 @@
1
+ import { FC, ReactNode } from "react";
2
+ import "./OutputField.scss";
3
+ export type Props = {
4
+ /**
5
+ * The id of the output element. This is used to associate the label with the output element for form or accessibility purposes.
6
+ */
7
+ id: string;
8
+ /**
9
+ * The label for the output field.
10
+ */
11
+ label: string;
12
+ /**
13
+ * The value to be displayed in the output field.
14
+ */
15
+ value: string;
16
+ /**
17
+ * Optional help text to provide additional information about the output field.
18
+ */
19
+ help?: ReactNode;
20
+ /**
21
+ * Whether the output field is required.
22
+ */
23
+ required?: boolean;
24
+ };
25
+ /**
26
+ *
27
+ * Use output fields to display read-only information with a label and optional help text. Output fields are ideal for displaying calculated values or results of user input in a consistent format.
28
+ */
29
+ export declare const OutputField: FC<Props>;
30
+ export default OutputField;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.OutputField = void 0;
7
+ var _propTypes = _interopRequireDefault(require("prop-types"));
8
+ var _react = _interopRequireDefault(require("react"));
9
+ var _Field = _interopRequireDefault(require("../Field"));
10
+ require("./OutputField.scss");
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ /**
13
+ *
14
+ * Use output fields to display read-only information with a label and optional help text. Output fields are ideal for displaying calculated values or results of user input in a consistent format.
15
+ */
16
+ const OutputField = _ref => {
17
+ let {
18
+ id,
19
+ label,
20
+ value,
21
+ help,
22
+ required
23
+ } = _ref;
24
+ return /*#__PURE__*/_react.default.createElement(_Field.default, {
25
+ forId: id,
26
+ label: label,
27
+ help: help,
28
+ labelClassName: "u-no-margin--bottom",
29
+ className: "output-field",
30
+ required: required
31
+ }, /*#__PURE__*/_react.default.createElement("output", {
32
+ id: id,
33
+ className: "mono-font u-sv2"
34
+ }, /*#__PURE__*/_react.default.createElement("b", null, value)));
35
+ };
36
+ exports.OutputField = OutputField;
37
+ OutputField.propTypes = {
38
+ id: _propTypes.default.string.isRequired,
39
+ label: _propTypes.default.string.isRequired,
40
+ value: _propTypes.default.string.isRequired,
41
+ help: _propTypes.default.node,
42
+ required: _propTypes.default.bool
43
+ };
44
+ var _default = exports.default = OutputField;
@@ -0,0 +1,11 @@
1
+ @import "vanilla-framework";
2
+
3
+ .output-field {
4
+ .p-form-help-text {
5
+ margin-top: -0.65rem;
6
+ }
7
+
8
+ .p-form__control {
9
+ margin-top: 0.25rem;
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from "@storybook/react";
2
+ import OutputField from "./OutputField";
3
+ declare const meta: Meta<typeof OutputField>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof OutputField>;
6
+ export declare const Default: Story;
7
+ export declare const HelpText: Story;
8
+ export declare const Required: Story;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.Required = exports.HelpText = exports.Default = void 0;
7
+ var _OutputField = _interopRequireDefault(require("./OutputField"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ const meta = {
10
+ component: _OutputField.default,
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ label: {
14
+ control: {
15
+ type: "text"
16
+ }
17
+ },
18
+ id: {
19
+ control: {
20
+ type: "text"
21
+ }
22
+ },
23
+ value: {
24
+ control: {
25
+ type: "text"
26
+ }
27
+ },
28
+ help: {
29
+ control: {
30
+ type: "text"
31
+ }
32
+ },
33
+ required: {
34
+ control: {
35
+ type: "boolean"
36
+ }
37
+ }
38
+ },
39
+ args: {
40
+ label: "Label",
41
+ id: "output-field",
42
+ value: "Output value",
43
+ help: "",
44
+ required: false
45
+ }
46
+ };
47
+ var _default = exports.default = meta;
48
+ const Default = exports.Default = {
49
+ name: "Default"
50
+ };
51
+ const HelpText = exports.HelpText = {
52
+ name: "Help Text",
53
+ args: {
54
+ help: "This is an output field with text"
55
+ }
56
+ };
57
+ const Required = exports.Required = {
58
+ name: "Required",
59
+ args: {
60
+ required: true
61
+ }
62
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { default } from "./OutputField";
2
+ export type { Props as OutputFieldProps } from "./OutputField";
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "default", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _OutputField.default;
10
+ }
11
+ });
12
+ var _OutputField = _interopRequireDefault(require("./OutputField"));
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -50,7 +50,7 @@ const PrefixedIpInput = _ref => {
50
50
  }
51
51
  };
52
52
  return /*#__PURE__*/_react.default.createElement(_PrefixedInput.default, _extends({
53
- help: help ? help : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, " ", isIPV4 ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, " ", "The available range in this subnet is", " ", /*#__PURE__*/_react.default.createElement("code", null, immutable, ".", editable, " ")) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, " ", "The available IPV6 address range is", " ", /*#__PURE__*/_react.default.createElement("code", null, immutable, editable, " ")), "."),
53
+ help: help ? help : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, " ", isIPV4 ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, " ", "The available range in this subnet is", " ", /*#__PURE__*/_react.default.createElement("code", null, immutable, ".", editable, " ")) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, " ", "The subnet CIDR is ", /*#__PURE__*/_react.default.createElement("code", null, cidr)), "."),
54
54
  immutableText: isIPV4 ? "".concat(immutable, ".") : immutable,
55
55
  maxLength: maxLength,
56
56
  name: name,
@@ -14,6 +14,7 @@ export declare const isIPv4: (ip: string) => boolean;
14
14
  export declare const getIpRangeFromCidr: (cidr: string) => string[];
15
15
  export declare const getFirstValidIp: (ip: string) => string;
16
16
  export declare const convertIpToUint32: (ip: string) => number;
17
+ export declare const convertUint32ToIp: (ipAsUint32: number) => string;
17
18
  /**
18
19
  * Checks if an IPv4 address is valid for the given subnet.
19
20
  *
@@ -30,10 +31,18 @@ export declare const isIpInSubnet: (ip: string, cidr: string) => boolean;
30
31
  * @returns The immutable and editable octects as two strings in a list
31
32
  */
32
33
  export declare const getImmutableAndEditableOctets: (startIp: string, endIp: string) => string[];
34
+ /**
35
+ * Separates the immutable and editable parts of an IPv6 subnet range.
36
+ * For simplcity, if the prefix is not on a group boundary, the entire last group is considered editable.
37
+ *
38
+ * @param cidr The CIDR notation of the subnet
39
+ * @returns The immutable and editable parts as two strings in a list
40
+ */
41
+ export declare const getImmutableAndEditableIPv6: (cidr: string) => string[];
33
42
  /**
34
43
  * Get the immutable and editable parts of an IPv4 or IPv6 subnet.
35
44
  *
36
45
  * @param cidr The CIDR notation of the subnet
37
- * @returns The immutable and editable as two strings in a list
46
+ * @returns The immutable and editable as two strings in a list
38
47
  */
39
48
  export declare const getImmutableAndEditable: (cidr: string) => string[];
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isIpInSubnet = exports.isIPv4 = exports.getIpRangeFromCidr = exports.getImmutableAndEditableOctets = exports.getImmutableAndEditable = exports.getFirstValidIp = exports.convertIpToUint32 = void 0;
6
+ exports.isIpInSubnet = exports.isIPv4 = exports.getIpRangeFromCidr = exports.getImmutableAndEditableOctets = exports.getImmutableAndEditableIPv6 = exports.getImmutableAndEditable = exports.getFirstValidIp = exports.convertUint32ToIp = exports.convertIpToUint32 = void 0;
7
7
  /**
8
8
  * Checks if a given IP address is a valid IPv4 address.
9
9
  * @param ip The IP address to check
@@ -26,8 +26,11 @@ const getIpRangeFromCidr = cidr => {
26
26
  // https://gist.github.com/binarymax/6114792
27
27
 
28
28
  // Get start IP and number of valid addresses
29
- const [startIp, mask] = cidr.split("/");
30
- const numberOfAddresses = (1 << 32 - parseInt(mask)) - 1;
29
+ const [unmaskedStartIp, mask] = cidr.split("/");
30
+ const maskBits = parseInt(mask, 10);
31
+ const subnetMask = maskBits === 0 ? 0 : 0xffffffff << 32 - maskBits >>> 0;
32
+ const startIp = convertUint32ToIp(convertIpToUint32(unmaskedStartIp) & subnetMask);
33
+ const numberOfAddresses = (1 << 32 - maskBits) - 1;
31
34
 
32
35
  // IPv4 can be represented by an unsigned 32-bit integer, so we can use a Uint32Array to store the IP
33
36
  const buffer = new ArrayBuffer(4); //4 octets
@@ -63,6 +66,14 @@ const convertIpToUint32 = ip => {
63
66
  int32[0] = (octets[0] << 24) + (octets[1] << 16) + (octets[2] << 8) + octets[3];
64
67
  return int32[0];
65
68
  };
69
+ exports.convertIpToUint32 = convertIpToUint32;
70
+ const convertUint32ToIp = ipAsUint32 => {
71
+ const buffer = new ArrayBuffer(4); //4 octets
72
+ const int32 = new Uint32Array(buffer);
73
+ int32[0] = ipAsUint32;
74
+ const octets = Array.from(new Uint8Array(buffer));
75
+ return octets.reverse().join(".");
76
+ };
66
77
 
67
78
  /**
68
79
  * Checks if an IPv4 address is valid for the given subnet.
@@ -71,7 +82,7 @@ const convertIpToUint32 = ip => {
71
82
  * @param cidr The subnet's CIDR notation e.g. 192.168.0.0/24
72
83
  * @returns True if the IP is in the subnet, false otherwise
73
84
  */
74
- exports.convertIpToUint32 = convertIpToUint32;
85
+ exports.convertUint32ToIp = convertUint32ToIp;
75
86
  const isIpInSubnet = (ip, cidr) => {
76
87
  const [startIP, endIP] = getIpRangeFromCidr(cidr);
77
88
  const ipUint32 = convertIpToUint32(ip);
@@ -104,22 +115,43 @@ const getImmutableAndEditableOctets = (startIp, endIp) => {
104
115
  };
105
116
 
106
117
  /**
107
- * Get the immutable and editable parts of an IPv4 or IPv6 subnet.
118
+ * Separates the immutable and editable parts of an IPv6 subnet range.
119
+ * For simplcity, if the prefix is not on a group boundary, the entire last group is considered editable.
108
120
  *
109
121
  * @param cidr The CIDR notation of the subnet
110
- * @returns The immutable and editable as two strings in a list
122
+ * @returns The immutable and editable parts as two strings in a list
111
123
  */
112
124
  exports.getImmutableAndEditableOctets = getImmutableAndEditableOctets;
125
+ const getImmutableAndEditableIPv6 = cidr => {
126
+ const [address, prefix] = cidr.split("/");
127
+ const prefixLength = parseInt(prefix, 10);
128
+ const [left = "", right = ""] = address.split("::");
129
+ const leftGroups = left ? left.split(":").filter(Boolean) : [];
130
+ const rightGroups = right ? right.split(":").filter(Boolean) : [];
131
+ const missingGroups = Math.max(0, 8 - (leftGroups.length + rightGroups.length));
132
+ const expandedGroups = [...leftGroups, ...Array(missingGroups).fill("0"), ...Array(rightGroups.length).fill("0")];
133
+ const immutableGroupCount = Math.floor(prefixLength / 16);
134
+ let immutableIPV6 = immutableGroupCount > 0 ? "".concat(expandedGroups.slice(0, immutableGroupCount).join(":")) : "";
135
+ if (immutableGroupCount < 8) {
136
+ immutableIPV6 += immutableIPV6 ? ":" : "";
137
+ }
138
+ const editableIPV6 = "".concat(expandedGroups.slice(immutableGroupCount).join(":"));
139
+ return [immutableIPV6, editableIPV6];
140
+ };
141
+
142
+ /**
143
+ * Get the immutable and editable parts of an IPv4 or IPv6 subnet.
144
+ *
145
+ * @param cidr The CIDR notation of the subnet
146
+ * @returns The immutable and editable as two strings in a list
147
+ */
148
+ exports.getImmutableAndEditableIPv6 = getImmutableAndEditableIPv6;
113
149
  const getImmutableAndEditable = cidr => {
114
150
  const isIPV4 = isIPv4(cidr.split("/")[0]);
115
151
  if (isIPV4) {
116
152
  const [startIp, endIp] = getIpRangeFromCidr(cidr);
117
153
  return getImmutableAndEditableOctets(startIp, endIp);
118
154
  }
119
- const [networkAddress] = cidr.split("/");
120
- const immutableIPV6 = networkAddress.substring(0, networkAddress.lastIndexOf(":"));
121
- const ipv6PlaceholderColons = 7 - (immutableIPV6.match(/:/g) || []).length; // 7 is the maximum number of colons in an IPv6 address
122
- const editableIPV6 = "".concat("0000:".repeat(ipv6PlaceholderColons), "0000");
123
- return [immutableIPV6, editableIPV6];
155
+ return getImmutableAndEditableIPv6(cidr);
124
156
  };
125
157
  exports.getImmutableAndEditable = getImmutableAndEditable;
@@ -0,0 +1,30 @@
1
+ import { FC, ReactNode } from "react";
2
+ import "./OutputField.scss";
3
+ export type Props = {
4
+ /**
5
+ * The id of the output element. This is used to associate the label with the output element for form or accessibility purposes.
6
+ */
7
+ id: string;
8
+ /**
9
+ * The label for the output field.
10
+ */
11
+ label: string;
12
+ /**
13
+ * The value to be displayed in the output field.
14
+ */
15
+ value: string;
16
+ /**
17
+ * Optional help text to provide additional information about the output field.
18
+ */
19
+ help?: ReactNode;
20
+ /**
21
+ * Whether the output field is required.
22
+ */
23
+ required?: boolean;
24
+ };
25
+ /**
26
+ *
27
+ * Use output fields to display read-only information with a label and optional help text. Output fields are ideal for displaying calculated values or results of user input in a consistent format.
28
+ */
29
+ export declare const OutputField: FC<Props>;
30
+ export default OutputField;
@@ -0,0 +1,36 @@
1
+ import _pt from "prop-types";
2
+ import React from "react";
3
+ import Field from "../Field";
4
+ import "./OutputField.scss";
5
+ /**
6
+ *
7
+ * Use output fields to display read-only information with a label and optional help text. Output fields are ideal for displaying calculated values or results of user input in a consistent format.
8
+ */
9
+ export var OutputField = _ref => {
10
+ var {
11
+ id,
12
+ label,
13
+ value,
14
+ help,
15
+ required
16
+ } = _ref;
17
+ return /*#__PURE__*/React.createElement(Field, {
18
+ forId: id,
19
+ label: label,
20
+ help: help,
21
+ labelClassName: "u-no-margin--bottom",
22
+ className: "output-field",
23
+ required: required
24
+ }, /*#__PURE__*/React.createElement("output", {
25
+ id: id,
26
+ className: "mono-font u-sv2"
27
+ }, /*#__PURE__*/React.createElement("b", null, value)));
28
+ };
29
+ OutputField.propTypes = {
30
+ id: _pt.string.isRequired,
31
+ label: _pt.string.isRequired,
32
+ value: _pt.string.isRequired,
33
+ help: _pt.node,
34
+ required: _pt.bool
35
+ };
36
+ export default OutputField;
@@ -0,0 +1,11 @@
1
+ @import "vanilla-framework";
2
+
3
+ .output-field {
4
+ .p-form-help-text {
5
+ margin-top: -0.65rem;
6
+ }
7
+
8
+ .p-form__control {
9
+ margin-top: 0.25rem;
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from "@storybook/react";
2
+ import OutputField from "./OutputField";
3
+ declare const meta: Meta<typeof OutputField>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof OutputField>;
6
+ export declare const Default: Story;
7
+ export declare const HelpText: Story;
8
+ export declare const Required: Story;
@@ -0,0 +1,55 @@
1
+ import OutputField from "./OutputField";
2
+ var meta = {
3
+ component: OutputField,
4
+ tags: ["autodocs"],
5
+ argTypes: {
6
+ label: {
7
+ control: {
8
+ type: "text"
9
+ }
10
+ },
11
+ id: {
12
+ control: {
13
+ type: "text"
14
+ }
15
+ },
16
+ value: {
17
+ control: {
18
+ type: "text"
19
+ }
20
+ },
21
+ help: {
22
+ control: {
23
+ type: "text"
24
+ }
25
+ },
26
+ required: {
27
+ control: {
28
+ type: "boolean"
29
+ }
30
+ }
31
+ },
32
+ args: {
33
+ label: "Label",
34
+ id: "output-field",
35
+ value: "Output value",
36
+ help: "",
37
+ required: false
38
+ }
39
+ };
40
+ export default meta;
41
+ export var Default = {
42
+ name: "Default"
43
+ };
44
+ export var HelpText = {
45
+ name: "Help Text",
46
+ args: {
47
+ help: "This is an output field with text"
48
+ }
49
+ };
50
+ export var Required = {
51
+ name: "Required",
52
+ args: {
53
+ required: true
54
+ }
55
+ };
@@ -0,0 +1,2 @@
1
+ export { default } from "./OutputField";
2
+ export type { Props as OutputFieldProps } from "./OutputField";
@@ -0,0 +1 @@
1
+ export { default } from "./OutputField";
@@ -43,7 +43,7 @@ var PrefixedIpInput = _ref => {
43
43
  }
44
44
  };
45
45
  return /*#__PURE__*/React.createElement(PrefixedInput, _extends({
46
- help: help ? help : /*#__PURE__*/React.createElement(React.Fragment, null, " ", isIPV4 ? /*#__PURE__*/React.createElement(React.Fragment, null, " ", "The available range in this subnet is", " ", /*#__PURE__*/React.createElement("code", null, immutable, ".", editable, " ")) : /*#__PURE__*/React.createElement(React.Fragment, null, " ", "The available IPV6 address range is", " ", /*#__PURE__*/React.createElement("code", null, immutable, editable, " ")), "."),
46
+ help: help ? help : /*#__PURE__*/React.createElement(React.Fragment, null, " ", isIPV4 ? /*#__PURE__*/React.createElement(React.Fragment, null, " ", "The available range in this subnet is", " ", /*#__PURE__*/React.createElement("code", null, immutable, ".", editable, " ")) : /*#__PURE__*/React.createElement(React.Fragment, null, " ", "The subnet CIDR is ", /*#__PURE__*/React.createElement("code", null, cidr)), "."),
47
47
  immutableText: isIPV4 ? "".concat(immutable, ".") : immutable,
48
48
  maxLength: maxLength,
49
49
  name: name,
@@ -14,6 +14,7 @@ export declare const isIPv4: (ip: string) => boolean;
14
14
  export declare const getIpRangeFromCidr: (cidr: string) => string[];
15
15
  export declare const getFirstValidIp: (ip: string) => string;
16
16
  export declare const convertIpToUint32: (ip: string) => number;
17
+ export declare const convertUint32ToIp: (ipAsUint32: number) => string;
17
18
  /**
18
19
  * Checks if an IPv4 address is valid for the given subnet.
19
20
  *
@@ -30,10 +31,18 @@ export declare const isIpInSubnet: (ip: string, cidr: string) => boolean;
30
31
  * @returns The immutable and editable octects as two strings in a list
31
32
  */
32
33
  export declare const getImmutableAndEditableOctets: (startIp: string, endIp: string) => string[];
34
+ /**
35
+ * Separates the immutable and editable parts of an IPv6 subnet range.
36
+ * For simplcity, if the prefix is not on a group boundary, the entire last group is considered editable.
37
+ *
38
+ * @param cidr The CIDR notation of the subnet
39
+ * @returns The immutable and editable parts as two strings in a list
40
+ */
41
+ export declare const getImmutableAndEditableIPv6: (cidr: string) => string[];
33
42
  /**
34
43
  * Get the immutable and editable parts of an IPv4 or IPv6 subnet.
35
44
  *
36
45
  * @param cidr The CIDR notation of the subnet
37
- * @returns The immutable and editable as two strings in a list
46
+ * @returns The immutable and editable as two strings in a list
38
47
  */
39
48
  export declare const getImmutableAndEditable: (cidr: string) => string[];
@@ -19,8 +19,11 @@ export var getIpRangeFromCidr = cidr => {
19
19
  // https://gist.github.com/binarymax/6114792
20
20
 
21
21
  // Get start IP and number of valid addresses
22
- var [startIp, mask] = cidr.split("/");
23
- var numberOfAddresses = (1 << 32 - parseInt(mask)) - 1;
22
+ var [unmaskedStartIp, mask] = cidr.split("/");
23
+ var maskBits = parseInt(mask, 10);
24
+ var subnetMask = maskBits === 0 ? 0 : 0xffffffff << 32 - maskBits >>> 0;
25
+ var startIp = convertUint32ToIp(convertIpToUint32(unmaskedStartIp) & subnetMask);
26
+ var numberOfAddresses = (1 << 32 - maskBits) - 1;
24
27
 
25
28
  // IPv4 can be represented by an unsigned 32-bit integer, so we can use a Uint32Array to store the IP
26
29
  var buffer = new ArrayBuffer(4); //4 octets
@@ -54,6 +57,13 @@ export var convertIpToUint32 = ip => {
54
57
  int32[0] = (octets[0] << 24) + (octets[1] << 16) + (octets[2] << 8) + octets[3];
55
58
  return int32[0];
56
59
  };
60
+ export var convertUint32ToIp = ipAsUint32 => {
61
+ var buffer = new ArrayBuffer(4); //4 octets
62
+ var int32 = new Uint32Array(buffer);
63
+ int32[0] = ipAsUint32;
64
+ var octets = Array.from(new Uint8Array(buffer));
65
+ return octets.reverse().join(".");
66
+ };
57
67
 
58
68
  /**
59
69
  * Checks if an IPv4 address is valid for the given subnet.
@@ -92,11 +102,35 @@ export var getImmutableAndEditableOctets = (startIp, endIp) => {
92
102
  return [immutable.join("."), editable.join(".")];
93
103
  };
94
104
 
105
+ /**
106
+ * Separates the immutable and editable parts of an IPv6 subnet range.
107
+ * For simplcity, if the prefix is not on a group boundary, the entire last group is considered editable.
108
+ *
109
+ * @param cidr The CIDR notation of the subnet
110
+ * @returns The immutable and editable parts as two strings in a list
111
+ */
112
+ export var getImmutableAndEditableIPv6 = cidr => {
113
+ var [address, prefix] = cidr.split("/");
114
+ var prefixLength = parseInt(prefix, 10);
115
+ var [left = "", right = ""] = address.split("::");
116
+ var leftGroups = left ? left.split(":").filter(Boolean) : [];
117
+ var rightGroups = right ? right.split(":").filter(Boolean) : [];
118
+ var missingGroups = Math.max(0, 8 - (leftGroups.length + rightGroups.length));
119
+ var expandedGroups = [...leftGroups, ...Array(missingGroups).fill("0"), ...Array(rightGroups.length).fill("0")];
120
+ var immutableGroupCount = Math.floor(prefixLength / 16);
121
+ var immutableIPV6 = immutableGroupCount > 0 ? "".concat(expandedGroups.slice(0, immutableGroupCount).join(":")) : "";
122
+ if (immutableGroupCount < 8) {
123
+ immutableIPV6 += immutableIPV6 ? ":" : "";
124
+ }
125
+ var editableIPV6 = "".concat(expandedGroups.slice(immutableGroupCount).join(":"));
126
+ return [immutableIPV6, editableIPV6];
127
+ };
128
+
95
129
  /**
96
130
  * Get the immutable and editable parts of an IPv4 or IPv6 subnet.
97
131
  *
98
132
  * @param cidr The CIDR notation of the subnet
99
- * @returns The immutable and editable as two strings in a list
133
+ * @returns The immutable and editable as two strings in a list
100
134
  */
101
135
  export var getImmutableAndEditable = cidr => {
102
136
  var isIPV4 = isIPv4(cidr.split("/")[0]);
@@ -104,9 +138,5 @@ export var getImmutableAndEditable = cidr => {
104
138
  var [startIp, endIp] = getIpRangeFromCidr(cidr);
105
139
  return getImmutableAndEditableOctets(startIp, endIp);
106
140
  }
107
- var [networkAddress] = cidr.split("/");
108
- var immutableIPV6 = networkAddress.substring(0, networkAddress.lastIndexOf(":"));
109
- var ipv6PlaceholderColons = 7 - (immutableIPV6.match(/:/g) || []).length; // 7 is the maximum number of colons in an IPv6 address
110
- var editableIPV6 = "".concat("0000:".repeat(ipv6PlaceholderColons), "0000");
111
- return [immutableIPV6, editableIPV6];
141
+ return getImmutableAndEditableIPv6(cidr);
112
142
  };
@@ -40,6 +40,7 @@ export * from "./components/MultiSelect";
40
40
  export { default as Notification, NotificationSeverity, } from "./components/Notifications";
41
41
  export { NotificationConsumer, NotificationProvider, useNotify, info, success, failure, queue, } from "./components/NotificationProvider";
42
42
  export { default as LoginPageLayout } from "./components/LoginPageLayout";
43
+ export { default as OutputField } from "./components/OutputField";
43
44
  export { default as Pagination } from "./components/Pagination";
44
45
  export { default as Panel } from "./components/Panel";
45
46
  export { default as PasswordToggle } from "./components/PasswordToggle";
@@ -118,6 +119,7 @@ export type { GenerateLink, LogoProps, NavigationProps, NavItem, NavLink, NavLin
118
119
  export type { NotificationProps } from "./components/Notifications";
119
120
  export type { NotificationAction, NotificationType, QueuedNotification, NotificationHelper, } from "./components/NotificationProvider";
120
121
  export type { LoginPageLayoutProps } from "./components/LoginPageLayout";
122
+ export type { OutputFieldProps } from "./components/OutputField";
121
123
  export type { PaginationProps } from "./components/Pagination";
122
124
  export type { PanelProps } from "./components/Panel";
123
125
  export type { PrefixedInputProps } from "./components/PrefixedInput";
package/dist/esm/index.js CHANGED
@@ -40,6 +40,7 @@ export * from "./components/MultiSelect";
40
40
  export { default as Notification, NotificationSeverity } from "./components/Notifications";
41
41
  export { NotificationConsumer, NotificationProvider, useNotify, info, success, failure, queue } from "./components/NotificationProvider";
42
42
  export { default as LoginPageLayout } from "./components/LoginPageLayout";
43
+ export { default as OutputField } from "./components/OutputField";
43
44
  export { default as Pagination } from "./components/Pagination";
44
45
  export { default as Panel } from "./components/Panel";
45
46
  export { default as PasswordToggle } from "./components/PasswordToggle";
package/dist/index.d.ts CHANGED
@@ -40,6 +40,7 @@ export * from "./components/MultiSelect";
40
40
  export { default as Notification, NotificationSeverity, } from "./components/Notifications";
41
41
  export { NotificationConsumer, NotificationProvider, useNotify, info, success, failure, queue, } from "./components/NotificationProvider";
42
42
  export { default as LoginPageLayout } from "./components/LoginPageLayout";
43
+ export { default as OutputField } from "./components/OutputField";
43
44
  export { default as Pagination } from "./components/Pagination";
44
45
  export { default as Panel } from "./components/Panel";
45
46
  export { default as PasswordToggle } from "./components/PasswordToggle";
@@ -118,6 +119,7 @@ export type { GenerateLink, LogoProps, NavigationProps, NavItem, NavLink, NavLin
118
119
  export type { NotificationProps } from "./components/Notifications";
119
120
  export type { NotificationAction, NotificationType, QueuedNotification, NotificationHelper, } from "./components/NotificationProvider";
120
121
  export type { LoginPageLayoutProps } from "./components/LoginPageLayout";
122
+ export type { OutputFieldProps } from "./components/OutputField";
121
123
  export type { PaginationProps } from "./components/Pagination";
122
124
  export type { PanelProps } from "./components/Panel";
123
125
  export type { PrefixedInputProps } from "./components/PrefixedInput";
package/dist/index.js CHANGED
@@ -61,6 +61,7 @@ var _exportNames = {
61
61
  failure: true,
62
62
  queue: true,
63
63
  LoginPageLayout: true,
64
+ OutputField: true,
64
65
  Pagination: true,
65
66
  Panel: true,
66
67
  PasswordToggle: true,
@@ -410,6 +411,12 @@ Object.defineProperty(exports, "NotificationSeverity", {
410
411
  return _Notifications.NotificationSeverity;
411
412
  }
412
413
  });
414
+ Object.defineProperty(exports, "OutputField", {
415
+ enumerable: true,
416
+ get: function () {
417
+ return _OutputField.default;
418
+ }
419
+ });
413
420
  Object.defineProperty(exports, "Pagination", {
414
421
  enumerable: true,
415
422
  get: function () {
@@ -919,6 +926,7 @@ Object.keys(_MultiSelect).forEach(function (key) {
919
926
  var _Notifications = _interopRequireWildcard(require("./components/Notifications"));
920
927
  var _NotificationProvider = require("./components/NotificationProvider");
921
928
  var _LoginPageLayout = _interopRequireDefault(require("./components/LoginPageLayout"));
929
+ var _OutputField = _interopRequireDefault(require("./components/OutputField"));
922
930
  var _Pagination = _interopRequireDefault(require("./components/Pagination"));
923
931
  var _Panel = _interopRequireDefault(require("./components/Panel"));
924
932
  var _PasswordToggle = _interopRequireDefault(require("./components/PasswordToggle"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": {
@@ -96,7 +96,7 @@
96
96
  "tsc-alias": "1.8.10",
97
97
  "typescript": "5.7.3",
98
98
  "typescript-eslint": "8.24.1",
99
- "vanilla-framework": "4.46.0",
99
+ "vanilla-framework": "4.48.0",
100
100
  "wait-on": "9.0.3",
101
101
  "webpack": "5.105.0"
102
102
  },