@activecollab/components
Version:
ActiveCollab Components
1,098 lines (1,054 loc) • 1.1 MB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('classnames'), require('styled-components'), require('resize-observer-polyfill'), require('react-focus-lock'), require('react-remove-scroll'), require('react-dom'), require('@popperjs/core'), require('react-transition-group'), require('moment-timezone'), require('@tanstack/react-virtual'), require('country-explorer'), require('compromise'), require('compromise-dates'), require('moment')) :
typeof define === 'function' && define.amd ? define(['exports', 'react', 'classnames', 'styled-components', 'resize-observer-polyfill', 'react-focus-lock', 'react-remove-scroll', 'react-dom', '@popperjs/core', 'react-transition-group', 'moment-timezone', '@tanstack/react-virtual', 'country-explorer', 'compromise', 'compromise-dates', 'moment'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.components = {}, global.React, global.classNames, global.styled, global.ResizeObserver, global.FocusLock, global.reactRemoveScroll, global.ReactDOM, global.PopperJS, global.reactTransitionGroup, global["moment-timezone"], global.reactVirtual, global.countryExplorer, global.compromise, global.compromiseDates, global.moment));
})(this, (function (exports, React, classNames, styled, ResizeObserver, FocusLock, reactRemoveScroll, ReactDOM, core, reactTransitionGroup, moment, reactVirtual, countryExplorer, nlp, plg, moment$1) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames);
var styled__default = /*#__PURE__*/_interopDefaultLegacy(styled);
var ResizeObserver__default = /*#__PURE__*/_interopDefaultLegacy(ResizeObserver);
var FocusLock__default = /*#__PURE__*/_interopDefaultLegacy(FocusLock);
var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
var nlp__default = /*#__PURE__*/_interopDefaultLegacy(nlp);
var plg__default = /*#__PURE__*/_interopDefaultLegacy(plg);
var moment__default$1 = /*#__PURE__*/_interopDefaultLegacy(moment$1);
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) {
if (Object(t) !== t) return;
f = !1;
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : String(i);
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var BoxSizingStyle = styled.css(["box-sizing:border-box;*{box-sizing:border-box;}"]);
var FontStyle = styled.css(["font-family:-apple-system,BlinkMacSystemFont,\"Roboto\",\"Helvetica Neue\",Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;"]);
var StyledButton$2 = styled__default["default"].button.withConfig({
displayName: "Styles__StyledButton",
componentId: "sc-vi1kcr-0"
})(["", " ", " -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;vertical-align:middle;font-weight:500;display:inline-block;line-height:1;margin:0;text-decoration:none;font-size:0.875rem;user-select:none;cursor:pointer;text-align:center;flex-shrink:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:288px;border:none;height:32px;transition:background-color 0.3s ease,box-shadow 0.3s ease,color 0.3s ease,border-color 0.3s ease;svg{fill:currentColor;}&:focus{outline:none;}&:hover{text-decoration:none;}&:disabled{cursor:default;opacity:50%;pointer-events:none;}", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", ""], FontStyle, BoxSizingStyle, function (props) {
return props.size === "small" && styled.css(["height:24px;"]);
}, function (props) {
return props.size === "big" && styled.css(["height:40px;"]);
}, function (props) {
return (props.variant === "primary" || props.variant === "contained") && styled.css(["padding:0 16px;background-color:var(--color-primary);border-radius:var(--ac-br-rounded);color:var(--page-paper-main);&:hover,&:focus-visible{box-shadow:0 3px 6px -2px var(--color-primary-500);background-color:var(--color-primary-800);}&:active{box-shadow:0 4px 10px -2px var(--color-primary-600);background-color:var(--color-primary-800);}"]);
}, function (props) {
return (props.variant === "secondary" || props.variant === "outlined") && styled.css(["padding:0 16px;background-color:transparent;border-radius:var(--ac-br-rounded);border:solid 1px var(--color-theme-700);color:var(--color-theme-700);&:hover,&:focus-visible{border-color:var(--color-primary);color:var(--color-primary);}&:active{border-color:var(--color-primary);color:var(--color-primary);background-color:var(--color-primary-200);}"]);
}, function (props) {
return props.variant === "dark transparent" && styled.css(["padding:0 8px;background-color:rgba(0,0,0,0.5);border-radius:var(--ac-br-6);color:var(--only-white);&:hover,&:focus-visible{background-color:rgba(0,0,0,0.85);}&:active{background-color:var(--only-black);}"]);
}, function (props) {
return (props.variant === "tertiary" || props.variant === "text colored") && styled.css(["padding:0 8px;background-color:transparent;border-radius:var(--ac-br-6);color:var(--color-primary);&:hover,&:focus-visible{background-color:var(--color-primary-200);}&:active{background-color:var(--color-primary-300);}"]);
}, function (props) {
return (props.variant === "option" || props.variant === "text gray") && styled.css(["padding:0 8px;background-color:transparent;border-radius:var(--ac-br-6);color:var(--color-theme-700);&:hover,&:focus-visible{background-color:var(--color-theme-300);color:var(--color-theme-900);}&:active{background-color:var(--color-theme-400);color:var(--color-theme-900);}"]);
}, function (props) {
return props.variant === "circle raised" && styled.css(["padding:0 6px;background-color:transparent;border-radius:var(--ac-br-rounded);color:var(--color-theme-700);&:hover,&:focus-visible{background-color:var(--page-paper-main);box-shadow:var(--shadow-tertiary);}&:active{background-color:var(--page-paper-main);box-shadow:var(--shadow-tertiary-hover);}"]);
}, function (props) {
return props.iconOnly && styled.css(["display:inline-flex;justify-content:center;align-items:center;padding:0;width:32px;", ";", " ", ""], (props.size === "small" || props.size === "big") && styled.css(["border-radius:var(--ac-br-rounded);"]), props.size === "small" && styled.css(["width:24px;"]), props.size === "big" && styled.css(["width:40px;"]));
}, function (props) {
return (props.variant === "primary" || props.variant === "contained" || props.variant === "secondary" || props.variant === "outlined") && styled.css([".c-btn__elements__element:first-child svg{margin-left:-6px;}.c-btn__elements__element:last-child svg{margin-right:-6px;}", ""], (props.size === "small" || props.size === "big") && styled.css(["border-radius:var(--ac-br-rounded);"]));
}, function (props) {
return (props.variant === "tertiary" || props.variant === "text colored" || props.variant === "option" || props.variant === "text gray" || props.variant === "dark transparent") && styled.css([".c-btn__elements__element:first-child svg{margin-left:-4px;}.c-btn__elements__element:last-child svg{margin-right:-4px;}", " ", ""], props.size === "small" && styled.css(["border-radius:var(--ac-br-4);"]), props.size === "big" && styled.css(["border-radius:var(--ac-br-8);"]));
}, function (props) {
return props.active && styled.css(["", " ", " ", " ", " ", " ", ""], (props.variant === "primary" || props.variant === "contained") && styled.css(["background-color:var(--color-primary-800);"]), (props.variant === "secondary" || props.variant === "outlined") && styled.css(["border-color:var(--color-primary);color:var(--color-primary);"]), props.variant === "dark transparent" && styled.css(["background-color:var(--only-black);"]), props.variant === "circle raised" && styled.css(["background-color:var(--page-paper-main);box-shadow:var(--shadow-tertiary-hover);"]), (props.variant === "tertiary" || props.variant === "text colored") && styled.css(["background-color:var(--color-primary-200);"]), (props.variant === "option" || props.variant === "text gray") && styled.css(["color:var(--color-primary);&:hover,&:focus-visible{color:var(--color-primary);}", ""], props.children instanceof Array && styled.css(["background-color:var(--color-theme-300);"])));
});
StyledButton$2.displayName = "StyledButton";
var StyledButtonElements = styled__default["default"].span.withConfig({
displayName: "Styles__StyledButtonElements",
componentId: "sc-vi1kcr-1"
})(["display:flex;align-items:center;"]);
StyledButtonElements.displayName = "StyledButtonElements";
var StyledButtonElement = styled__default["default"].span.withConfig({
displayName: "Styles__StyledButtonElement",
componentId: "sc-vi1kcr-2"
})(["display:inline-flex;svg{margin:0 4px;}"]);
StyledButtonElement.displayName = "StyledButtonElement";
var _excluded$4P = ["children", "active", "variant", "size", "className"];
/**
* @component Button
* @description
* Button component is used to trigger an action or event, such as submitting a form,
* opening a Dialog, canceling an action, or performing a delete operation.
*
* @prop {variant} - adds unique look and feel to button.
* @prop {size} - controls the size of a button.
* @prop {active} - adds active/pressed state to a button.
*
* @example
* return (
* <Button className="mr-2" variant="primary" size="big">
* Save
* </Button>
* )
* @see
* https://system.activecollab.com/?path=/story/components-button-indicators-button--button
* https://design.activecollab.com/docs/components/button
*/
var Button = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
var children = _ref.children,
_ref$active = _ref.active,
active = _ref$active === void 0 ? false : _ref$active,
_ref$variant = _ref.variant,
variant = _ref$variant === void 0 ? "contained" : _ref$variant,
_ref$size = _ref.size,
size = _ref$size === void 0 ? "medium" : _ref$size,
className = _ref.className,
args = _objectWithoutProperties(_ref, _excluded$4P);
return /*#__PURE__*/React__default["default"].createElement(StyledButton$2, _extends({
className: classNames__default["default"]("c-btn", className, {
"c-btn--contained": variant === "primary" || variant === "contained",
"c-btn--outlined": variant === "secondary" || variant === "outlined",
"c-btn--text_colored": variant === "tertiary" || variant === "text colored",
"c-btn--text_gray": variant === "option" || variant === "text gray",
"c-btn--dark_transparent": variant === "dark transparent",
"c-btn--circle-raised": variant === "circle raised",
"c-btn--big": size === "big",
"c-btn--small": size === "small",
"c-btn--icon-only": children instanceof Object && !(children instanceof Array)
}),
iconOnly: children instanceof Object && !(children instanceof Array),
variant: variant,
active: active,
size: size,
ref: ref,
"aria-pressed": active,
role: "button"
}, args), children instanceof Array ? /*#__PURE__*/React__default["default"].createElement(StyledButtonElements, {
className: "c-btn__elements"
}, children.map(function (value, index) {
return value ? /*#__PURE__*/React__default["default"].createElement(StyledButtonElement, {
className: "c-btn__elements__element",
key: index
}, value) : null;
})) : children);
});
Button.displayName = "Button";
var StyledGlobalAddButton = styled__default["default"](Button).withConfig({
displayName: "Styles__StyledGlobalAddButton",
componentId: "sc-7mpctc-0"
})(["svg{fill:var(--color-theme-100);}&:hover svg{transform:rotate(90deg);transition:ease 0.3s;}"]);
StyledGlobalAddButton.displayName = "StyledGlobalAddButton";
var GradientDefs = function GradientDefs(_ref) {
var _match$1$match, _parts$find;
var gradient = _ref.gradient;
if (!gradient) return null;
var match = gradient.match(/linear-gradient\((.*)\)$/);
if (!match) return null;
var parts = (_match$1$match = match[1].match(/(#[0-9a-fA-F]{3,6}|rgba?\([^)]*\)|[0-9.]+deg)/g)) !== null && _match$1$match !== void 0 ? _match$1$match : [];
var anglePart = (_parts$find = parts.find(function (p) {
return /\d+deg/.test(p);
})) !== null && _parts$find !== void 0 ? _parts$find : "0deg";
var angle = Number(anglePart.replace("deg", "")) || 0;
var colors = parts.filter(function (p) {
return /^#|rgb/.test(p);
});
if (colors.length < 2) return null;
return /*#__PURE__*/React__default["default"].createElement("defs", null, /*#__PURE__*/React__default["default"].createElement("linearGradient", {
id: "icon-gradient",
"data-testid": "icon-gradient",
gradientTransform: "rotate(".concat(angle, ")")
}, colors.map(function (color, i) {
return /*#__PURE__*/React__default["default"].createElement("stop", {
key: i,
offset: "".concat(i / (colors.length - 1) * 100, "%"),
stopColor: color
});
})));
};
var _excluded$4O = ["gradient"];
/**
* @component AddCrossTinyIcon
* @description
*
* The React Icon component is a visual element that displays an icon to represent a concept, object, or action.
* The Icon component is
* customizable, allowing for variations in size, color, and style to fit the needs of the application.
*
* @prop {string} [gradient] - Optional CSS linear-gradient string to apply a custom fill.
* Format: "linear-gradient(<angle>deg, <color1>, <color2>, ...)"
*
* @example
* return (
* <AddCrossTinyIcon gradient="linear-gradient(135deg, #4da2ed, #f72222)" />
* )
*
* @example
* return (
* <AddCrossTinyIcon className="mr-2" />
* )
*
* @see
* https://system.activecollab.com/?path=/story/foundation-icons-icons--icons
* @see
* https://design.activecollab.com/docs/foundations/icons
*/
var AddCrossTinyIcon = /*#__PURE__*/React__default["default"].forwardRef(function (_ref, svgRef) {
var gradient = _ref.gradient,
props = _objectWithoutProperties(_ref, _excluded$4O);
return /*#__PURE__*/React__default["default"].createElement("svg", _extends({
width: 24,
height: 24,
viewBox: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
"data-testid": "AddCrossTinyIcon",
fill: gradient ? "url(#icon-gradient)" : props !== null && props !== void 0 && props.fill ? props.fill : "var(--color-theme-600)",
focusable: false,
ref: svgRef
}, props), /*#__PURE__*/React__default["default"].createElement(GradientDefs, {
gradient: gradient
}), /*#__PURE__*/React__default["default"].createElement("path", {
d: "M12 4a1 1 0 011 1v6h6a1 1 0 010 2h-6v6a1 1 0 01-2 0l-.001-6H5a1 1 0 010-2h6V5a1 1 0 011-1z",
fillRule: "evenodd"
}));
});
AddCrossTinyIcon.displayName = "AddCrossTinyIcon";
var AddCrossTinyIcon$1 = AddCrossTinyIcon;
var _excluded$4N = ["className", "disabled"];
var GlobalAddButton = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
var className = _ref.className,
_ref$disabled = _ref.disabled,
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
rest = _objectWithoutProperties(_ref, _excluded$4N);
return /*#__PURE__*/React__default["default"].createElement(StyledGlobalAddButton, _extends({
ref: ref,
variant: "contained",
disabled: disabled,
className: classNames__default["default"]("c-global-add-btn", className)
}, rest), /*#__PURE__*/React__default["default"].createElement(AddCrossTinyIcon$1, null));
});
GlobalAddButton.displayName = "GlobalAddButton";
var StyledButtonGroup$1 = styled__default["default"].div.withConfig({
displayName: "Styles__StyledButtonGroup",
componentId: "sc-1vw4iq2-0"
})(["", " ", " ", " button{&:disabled{opacity:1;color:var(--color-theme-500);pointer-events:none;}&:first-child:not(:last-child){svg{margin-right:-4px;}", " ", "}&:last-child:not(:first-child){svg{margin-left:-4px;}", " ", "}&:not(:first-child):not(:last-child){", " ", "}}svg{", "}"], {
"display": "flex",
"flexDirection": "row"
}, FontStyle, BoxSizingStyle, {
"marginRight": "-1px",
"borderTopRightRadius": "0px",
"borderBottomRightRadius": "0px"
}, function (props) {
return props.$invalid && styled.css(["border-top-color:var(--red-alert) !important;border-bottom-color:var(--red-alert) !important;border-left-color:var(--red-alert) !important;"]);
}, {
"borderTopLeftRadius": "0px",
"borderBottomLeftRadius": "0px"
}, function (props) {
return props.$invalid && styled.css(["border-top-color:var(--red-alert) !important;border-bottom-color:var(--red-alert) !important;border-right-color:var(--red-alert) !important;"]);
}, {
"marginRight": "-1px",
"borderRadius": "0px"
}, function (props) {
return props.$invalid && styled.css(["border-top-color:var(--red-alert) !important;border-bottom-color:var(--red-alert) !important;"]);
}, {
"fill": "currentColor",
"color": "var(--color-theme-900)"
});
StyledButtonGroup$1.displayName = "StyledButtonGroup";
var _excluded$4M = ["children", "className", "invalid"];
var ButtonGroup = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
var children = _ref.children,
className = _ref.className,
invalid = _ref.invalid,
rest = _objectWithoutProperties(_ref, _excluded$4M);
return /*#__PURE__*/React__default["default"].createElement(StyledButtonGroup$1, _extends({
ref: ref,
className: classNames__default["default"]("c-btn-group", className),
$invalid: invalid
}, rest), children);
});
ButtonGroup.displayName = "ButtonGroup";
var StyledList$1 = styled__default["default"].ul.withConfig({
displayName: "Styles__StyledList",
componentId: "sc-dv1w0m-0"
})(["list-style-type:none;margin:0;padding:0;"]);
StyledList$1.displayName = "StyledList";
var StyledListItem$1 = styled__default["default"].li.withConfig({
displayName: "Styles__StyledListItem",
componentId: "sc-dv1w0m-1"
})(["", " ", " display:flex;flex-direction:row;align-items:center;cursor:pointer;padding:0 1rem;margin:0.25rem 0;font-size:0.875rem;user-select:none;color:var(--color-theme-900);line-height:28px;> svg:first-child{margin-right:8px;}&:hover,&:focus-visible{background-color:var(--color-theme-200);outline:none;}"], BoxSizingStyle, FontStyle);
StyledListItem$1.displayName = "StyledListItem";
var StyledListSeparator$1 = styled__default["default"].div.withConfig({
displayName: "Styles__StyledListSeparator",
componentId: "sc-dv1w0m-2"
})(["border-top:1px solid var(--border-primary);margin:12px 16px;height:1px;"]);
StyledListSeparator$1.displayName = "StyledListSeparator";
var _excluded$4L = ["children", "className"];
var ListItem = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
var children = _ref.children,
className = _ref.className,
props = _objectWithoutProperties(_ref, _excluded$4L);
return /*#__PURE__*/React__default["default"].createElement(StyledListItem$1, _extends({
className: classNames__default["default"]("c-list-item", className),
ref: ref,
tabIndex: 0
}, props), children);
});
ListItem.displayName = "ListItem";
var _excluded$4K = ["className"];
var ListSeparator = function ListSeparator(_ref) {
var className = _ref.className,
props = _objectWithoutProperties(_ref, _excluded$4K);
return /*#__PURE__*/React__default["default"].createElement(StyledListSeparator$1, _extends({
className: classNames__default["default"]("c-list-separator", className)
}, props));
};
ListSeparator.displayName = "ListSeparator";
var _excluded$4J = ["children", "className"];
var _List = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
var children = _ref.children,
className = _ref.className,
props = _objectWithoutProperties(_ref, _excluded$4J);
return /*#__PURE__*/React__default["default"].createElement(StyledList$1, _extends({
className: className,
ref: ref,
tabIndex: -1
}, props), children);
});
_List.displayName = "List";
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34757
var List = Object.assign({}, _List, {
Item: ListItem,
Separator: ListSeparator
});
var StyledNavElement = styled__default["default"].nav.withConfig({
displayName: "Styles__StyledNavElement",
componentId: "sc-rxaesd-0"
})(["", " ", " ", " ", ""], {
"width": "100%"
}, {
"overflow": "hidden"
}, FontStyle, BoxSizingStyle);
StyledNavElement.displayName = "StyledNavElement";
var StyledListWrapper = styled__default["default"].div.withConfig({
displayName: "Styles__StyledListWrapper",
componentId: "sc-rxaesd-1"
})(["", ""], {
"display": "inline-block"
});
StyledListWrapper.displayName = "StyledListWrapper";
var StyledOList = styled__default["default"].ol.withConfig({
displayName: "Styles__StyledOList",
componentId: "sc-rxaesd-2"
})(["", " ", " ", " ", " ", " ", ""], {
"display": "flex"
}, {
"alignItems": "flex-start"
}, {
"alignItems": "center"
}, {
"listStyleType": "none"
}, {
"padding": "0px"
}, {
"margin": "0px"
});
StyledOList.displayName = "StyledOList";
var StyledMenuList = styled__default["default"](List).withConfig({
displayName: "Styles__StyledMenuList",
componentId: "sc-rxaesd-3"
})(["", ""], {
"paddingTop": "0.5rem",
"paddingBottom": "0.5rem"
});
StyledMenuList.displayName = "StyledMenuList";
var StyledBreadcrumbListItem = styled__default["default"].li.withConfig({
displayName: "Styles__StyledBreadcrumbListItem",
componentId: "sc-rxaesd-4"
})(["", " ", ""], {
"display": "flex"
}, {
"flexShrink": "0"
});
StyledBreadcrumbListItem.displayName = "StyledBreadcrumbListItem";
var layers = {
cloud: 100000,
skyscraper: 1300,
tower: 1200,
house: 1000
};
var colors$1 = {
yellow: {
name: "Yellow",
color: "#FDF196",
darker: "#948D5D",
lighter: "#EBD429"
},
orange: {
name: "Orange",
color: "#FBBB75",
darker: "#916C43",
lighter: "#E99841"
},
beige: {
name: "Beige",
color: "#EAC2AD",
darker: "#877064",
lighter: "#B98A72"
},
pink: {
name: "Pink",
color: "#FBD6E7",
darker: "#917C86",
lighter: "#E4A1C0"
},
coral: {
name: "Coral",
color: "#FF9C9C",
darker: "#945A5A",
lighter: "#DB4545"
},
purpleDirty: {
name: "Purple dirty",
color: "#C49CB6",
darker: "#715A69",
lighter: "#B16E9A"
},
purple: {
name: "Purple",
color: "#BEACF9",
darker: "#6E6390",
lighter: "#9B86DF"
},
blueAqua: {
name: "Blue aqua",
color: "#BDF7FD",
darker: "#6D8F92",
lighter: "#7BDDE7"
},
bluePale: {
name: "Blue pale",
color: "#BEEAFF",
darker: "#6E8794",
lighter: "#92BFD4"
},
blueSky: {
name: "Blue sky",
color: "#A0CBFD",
darker: "#5C7592",
lighter: "#568CCB"
},
turquoise: {
name: "Turquoise",
color: "#B9E4E0",
darker: "#6B8482",
lighter: "#70BFB8"
},
greenPale: {
name: "Green pale",
color: "#C3E799",
darker: "#718658",
lighter: "#80C333"
},
greenOliveDrab: {
name: "Green olive drab",
color: "#98B57C",
darker: "#647157",
lighter: "#819F65"
},
graySilver: {
name: "Gray silver",
color: "#DDDDDD",
darker: "#808080",
lighter: "#ACACAC"
},
gray: {
name: "Gray",
color: "#CACACA",
darker: "#757575",
lighter: "#7D7D7D"
},
greenSuccess: {
name: "Green success",
color: "#64CD93"
},
redAlert: {
name: "Red alert",
color: "#ED6161"
},
yellowWarning: {
name: "Yellow warning",
color: "#FFCC6B"
}
};
var aiColors = {
aiGradient: {
name: "AI Gradient",
color: "linear-gradient(135deg, #4da2ed, #9f69e5, #ec43d8, #f72222)",
lighter: "linear-gradient(135deg,rgba(77, 162, 237, 0.1),rgba(159, 105, 229, 0.1),rgba(236, 67, 216, 0.1),rgba(230, 10, 10, 0.1))"
}
};
var currencyMultiplier = {
k: 1000,
m: 1000000,
b: 1000000000,
t: 1000000000000
};
var fixedDecimalSpaces = function fixedDecimalSpaces(num, fixed) {
fixed = fixed || 0;
fixed = Math.pow(10, fixed);
var parts = num.toString().split(".");
if (parts.length > 1 && parseInt(parts[1]) > fixed) {
return Math.floor(num * fixed) / fixed;
}
return Math.round(num * fixed) / fixed;
};
var getNumberFromString = function getNumberFromString(number) {
var thousandSeparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ",";
var decimalSeparator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ".";
if (typeof number === "string" && number === "") {
return NaN;
}
if (!isNaN(Number(number)) || typeof number === "number") {
return Number(number);
}
if (decimalSeparator === ",") {
var parts = number.split(decimalSeparator);
var result = 0;
if (parts.length === 2) {
result = parts[0] ? parseFloat(String(parts[0]).replaceAll(thousandSeparator, "")) : 0;
if (result < 0) {
result -= parts[1] ? parseFloat(String(parts[1])) / Math.pow(10, parts[1].length) : 0;
} else {
result += parts[1] ? parseFloat(String(parts[1])) / Math.pow(10, parts[1].length) : 0;
}
return result;
}
if (parts.length === 1) {
result = parseFloat(String(parts[0]).replaceAll(thousandSeparator, ""));
return result;
}
return 0;
} else {
return parseFloat(String(number).replaceAll(thousandSeparator, ""));
}
};
/**
* @function formatNumber
* @description
* Formats a number or string into a human-readable format with options for thousand separators,
* decimal precision, and shortening the number using suffixes (K, M, B, T).
* It can handle negative numbers and optionally trim decimals and set number of decimal places based on the configuration.
*
* @param {string | number} n - The number or string to be formatted.
* @param {"," | "." | " " | ""} [thousandSeparator=","] - The character to use as a thousand separator.
* @param {"," | "."} [decimalSeperator="."] - The character to use as a decimal separator.
* @param {boolean} [trimDecimals=true] - Whether to trim decimals to the specified number of decimal spaces.
* @param {number} [decimalSpaces=2] - The number of decimal spaces to keep in the formatted output.
* @param {"long" | "short"} [format="long"] - The format type, either "long" for full numbers or "short" for shortened numbers with suffixes.
* @param {number} [shortenThreshold=1000] - The minimum numeric value at which the number should be shortened
* using suffixes. Numbers below this threshold will always be shown in long format.
*
* @returns {string} - The formatted number as a string with separators and potentially with decimals.
*
* @example
* formatNumber(1500) // "1,500.00"
* formatNumber(1500000, ",", ".", true, 2, "short") // "1.5M"
*/
var formatNumber = function formatNumber(n) {
var thousandSeparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ",";
var decimalSeperator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ".";
var trimDecimals = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var decimalSpaces = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 2;
var format = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : "long";
var shortenThreshold = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 1000;
if (n === undefined || n === null) return "";
var number = getNumberFromString(n, thousandSeparator, decimalSeperator);
if (isNaN(number)) {
return "";
}
var isNegative = number < 0;
var absoluteNumber = Math.abs(number);
var suffixes = ["", "K", "M", "B", "T"];
var scale = 0;
var shouldShorten = absoluteNumber >= shortenThreshold && format === "short";
while (shouldShorten && scale < suffixes.length - 1 && absoluteNumber >= 1000) {
absoluteNumber /= 1000;
scale++;
}
var formattedNum = scale === 0 ? fixedDecimalSpaces(absoluteNumber, decimalSpaces) : parseFloat(absoluteNumber.toFixed(1));
if (formattedNum === 1000 && scale > 0 && scale < 4) {
formattedNum = 1;
scale++;
}
var result = numberWithSeparator(trimDecimals ? formattedNum : formattedNum.toFixed(decimalSpaces), thousandSeparator, decimalSeperator);
return isNegative ? "-".concat(result).concat(suffixes[scale]) : "".concat(result).concat(suffixes[scale]);
};
var numberWithSeparator = function numberWithSeparator(x) {
var thousandSeparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ",";
var decimalSeparator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ".";
var format = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
if (!format) {
return x;
}
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);
return parts.join(decimalSeparator);
};
/**
* @function formatCurrency
* @description
* Formats a number or string as a currency string with options for thousand separators,
* decimal precision, currency code, currency code position, and the use of suffixes (K, M, B, T) if needed.
* It can handle negative values and supports customization of the currency code and its position.
*
* @param {string | number} n - The number or string to be formatted as currency.
* @param {"," | "." | " " | ""} [thousandSeparator=","] - The character to use as a thousand separator.
* @param {"," | "."} [decimalSeperator="."] - The character to use as a decimal separator.
* @param {boolean} [trimDecimals=false] - Whether to trim decimals to the specified number of decimal spaces.
* @param {number} [decimalSpaces=2] - The number of decimal spaces to keep in the formatted output.
* @param {"long" | "short"} [format="long"] - The format type, either "long" for full numbers or "short" for shortened numbers with suffixes.
* @param {string} [currencyCode=""] - The currency code to append or prepend to the formatted number.
* @param {"right" | "left"} [currencyCodePosition="right"] - The position of the currency code relative to the formatted number.
*
* @returns {string} - The formatted currency string, including the currency code if provided.
*
* @example
* formatCurrency(1500, ",", ".", false, 2, "long", "USD", "right") // "1,500.00 USD"
* formatCurrency(1500000, ",", ".", true, 2, "short", "JPY", "left") // "JPY 1.5M"
*/
var formatCurrency = function formatCurrency(n) {
var thousandSeparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ",";
var decimalSeperator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ".";
var trimDecimals = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var decimalSpaces = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 2;
var format = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : "long";
var currencyCode = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : "";
var currencyCodePosition = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : "right";
var formattedNum = formatNumber(n, thousandSeparator, decimalSeperator, trimDecimals, decimalSpaces, format, 10000);
if (!currencyCode) {
return formattedNum;
}
return currencyCodePosition === "right" ? "".concat(formattedNum, " ").concat(currencyCode) : "".concat(currencyCode, " ").concat(formattedNum);
};
var validateStopwatchTime = function validateStopwatchTime(value) {
return /^([0-9]{0,2})?(((:([0-5][0-9])?)|(:[0-5]?))|(\.[0-9]{0,2})|(,[0-9]{0,2}))?$/g.test(value);
};
var validateTimeInput = function validateTimeInput(value, withLeadingZero) {
if (withLeadingZero) {
return /^(([0-9][0-9]?|[1-9][0-9]{2,8})?(([:,.][0-5][0-9]?)|([:,.][0-5]?)|(\.[0-9]{1,2})|(,[0-9]{1,2}))?)$/.test(value);
} else {
return /^(([1-9][0-9]*|0)?(([:,.][0-5][0-9]?)|([:,.][0-5]?)|(\.[0-9]{1,2})|(,[0-9]{1,2}))?)$/.test(value);
}
};
var validateNumberInput = function validateNumberInput(value, disableMacros, decimalSeparator, decimalLength) {
var limit = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 12;
if (value.startsWith("00")) {
return false;
}
var numericInput = disableMacros ? value : value.replace(/([0-9.]+)([kmbtKMBT])/, function (_, num, unit) {
return (parseFloat(num) * currencyMultiplier[unit.toLowerCase()]).toString();
});
var regexString = "^(-?\\d{0,".concat(limit, "}(?:\\").concat(decimalSeparator, "\\d{0,").concat(decimalLength, "})?)?$");
return new RegExp(regexString).test(numericInput);
};
var isValidUrl = function isValidUrl(string) {
try {
new URL(string);
return true;
} catch (_) {
return false;
}
};
function setRef(ref, value) {
if (typeof ref === "function") {
ref(value);
} else if (ref) {
ref.current = value;
}
}
function useForkRef(refA, refB) {
return React.useMemo(function () {
return function (refValue) {
setRef(refA, refValue);
setRef(refB, refValue);
};
}, [refA, refB]);
}
var useResizeObserver = function useResizeObserver(ref) {
var _useState = React.useState(),
_useState2 = _slicedToArray(_useState, 2),
dimensions = _useState2[0],
setDimensions = _useState2[1];
React.useEffect(function () {
var observeTarget = ref === null || ref === void 0 ? void 0 : ref.current;
var resizeObserver = new ResizeObserver__default["default"](function (entries) {
entries.forEach(function (entry) {
setDimensions(entry.contentRect);
});
});
observeTarget && resizeObserver.observe(observeTarget);
return function () {
observeTarget && resizeObserver.unobserve(observeTarget);
};
}, [ref]);
return dimensions;
};
var useResizeObserver$1 = useResizeObserver;
/**
* @function formatHours
* @description
* Formats a decimal number representing hours into a formatted string (HH:MM) or a short form if needed.
* The input can be a number, string, or undefined. It handles various formats and can optionally add a
* leading zero to the hours component. With the new "format" argument, if set to "short" and the value is
* greater than or equal to 1000, it will return a shortened format (e.g., 1K) similar to formatNumber.
*
* @param {number | string | undefined} num - The input representing the hours.
* @param {boolean} [withLeadingZeroHours=false] - Whether to add a leading zero to the hours part.
* @param {boolean} [trimZeroMinutes=false] - Whether to remove minutes if they are zero.
* @param {"long" | "short"} [format="long"] - The format type, either "long" for full numbers or "short" for abbreviated output.
*
* @returns {string} - A formatted time string or a shortened string.
*
* @example
* formatHours(1.5) // "1:30"
* formatHours("3.5", true) // "03:30"
* formatHours(1500, false, false, "short") // "1K" (using formatNumber)
*/
var formatHours = function formatHours(num) {
var withLeadingZeroHours = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var trimZeroMinutes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var format = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "long";
// Handle the explicit zero case.
if (num === 0 || num === "0") {
return trimZeroMinutes ? "0" : withLeadingZeroHours ? "00:00" : "0:00";
}
if (!num) {
return "";
}
// If the input is in colon format, process it as before.
if (typeof num === "string" && num.indexOf(":") >= 0) {
// eslint-disable-next-line prefer-const
var _num$split = num.split(":"),
_num$split2 = _slicedToArray(_num$split, 2),
_hours = _num$split2[0],
_minutes = _num$split2[1];
if (_minutes && _minutes.length === 1 && Number(_minutes) < 10) {
_minutes = "".concat(Number(_minutes), "0");
}
if (_hours && _minutes) {
if (trimZeroMinutes && _minutes === "00") {
return withLeadingZeroHours ? withLeadingZero(_hours) : String(Number(_hours));
}
return withLeadingZeroHours ? "".concat(withLeadingZero(_hours), ":").concat(_minutes) : "".concat(_hours, ":").concat(_minutes);
} else if (_hours && !_minutes) {
return withLeadingZeroHours ? "".concat(withLeadingZero(_hours), ":00") : "".concat(_hours, ":00");
} else if (!_hours && _minutes) {
return withLeadingZeroHours ? "00:".concat(_minutes) : "0:".concat(_minutes);
} else if (!_hours && !_minutes) {
return withLeadingZeroHours ? "00:00" : "0:00";
}
return withLeadingZeroHours ? "00:".concat(_minutes) : "0:".concat(_minutes);
}
// Replace comma with dot if needed.
if (typeof num === "string" && num.indexOf(",") >= 0) {
num = num.replace(",", ".");
}
var input = typeof num === "string" ? parseFloat(num) : num;
// Use short formatting if specified and the value is >= 1000.
if (format === "short" && input >= 1000) {
return formatNumber(input, ",", ".", true, 2, "short");
}
// For whole numbers.
if (!isDecimal(input)) {
if (trimZeroMinutes) {
return withLeadingZeroHours ? withLeadingZero(input) : String(Number(input));
}
return withLeadingZeroHours ? "".concat(withLeadingZero(input), ":00") : "".concat(input, ":00");
}
// Process decimal hours.
var decimal = input.toFixed(2);
var time = decimal.toString().split(".");
var hours = time[0];
if (withLeadingZeroHours) {
hours = withLeadingZero(hours);
}
var minutes = time[1];
var minutesFormatted = Math.round(parseInt(minutes, 10) / 100 * 60);
if (trimZeroMinutes && minutesFormatted === 0) {
return hours;
}
return "".concat(hours, ":").concat(withLeadingZero(minutesFormatted));
};
var withLeadingZero = function withLeadingZero(num) {
var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
var s = "".concat(num);
while (s.length < size) s = "0" + s;
return s;
};
var isDecimal = function isDecimal(num) {
return !Number.isInteger(num);
};
var isValidTime = function isValidTime(time) {
return time === undefined || /^([01]\d|2[0-3]):([0-5]\d)$/.test(time);
};
var StyledIconButton$1 = styled__default["default"](Button).withConfig({
displayName: "Styles__StyledIconButton",
componentId: "sc-1teza2f-0"
})(["display:inline-flex;justify-content:center;align-items:center;padding:0;width:32px;", " ", ""], function (_ref) {
var size = _ref.size;
return size === "small" && styled.css(["width:24px;svg{width:18px;height:18px;}"]);
}, function (_ref2) {
var size = _ref2.size;
return size === "big" && styled.css(["width:40px;"]);
});
var _excluded$4I = ["children", "className", "variant", "size", "active"];
// eslint-disable-next-line @typescript-eslint/no-empty-interface
/**
* @component IconButton
* @description
* IconButton composes the Button component except that it renders only an icon.
* Since IconButton only renders an icon, you have to pass the aria-label prop,
* so screen readers can give meaning to the button.
*
* @props See Button props.
*
* @example
* return (
* <IconButton className="mr-2" variant="primary" size="medium" aria-label="Close dialog">
* <CloseIcon />
* </IconButton>
* )
* @see
* https://system.activecollab.com/?path=/story/components-button-indicators-button--icon-button
* https://design.activecollab.com/docs/components/button
*/
var IconButton = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
var children = _ref.children,
className = _ref.className,
variant = _ref.variant,
size = _ref.size,
active = _ref.active,
args = _objectWithoutProperties(_ref, _excluded$4I);
return /*#__PURE__*/React__default["default"].createElement(StyledIconButton$1, _extends({
className: className,
variant: variant,
size: size,
active: active,
ref: ref
}, args), children);
});
IconButton.displayName = "IconButton";
var _excluded$4H = ["gradient"];
/**
* @component AccessLogIcon
* @description
*
* The React Icon component is a visual element that displays an icon to represent a concept, object, or action.
* The Icon component is
* customizable, allowing for variations in size, color, and style to fit the needs of the application.
*
* @prop {string} [gradient] - Optional CSS linear-gradient string to apply a custom fill.
* Format: "linear-gradient(<angle>deg, <color1>, <color2>, ...)"
*
* @example
* return (
* <AccessLogIcon gradient="linear-gradient(135deg, #4da2ed, #f72222)" />
* )
*
* @example
* return (
* <AccessLogIcon className="mr-2" />
* )
*
* @see
* https://system.activecollab.com/?path=/story/foundation-icons-icons--icons
* @see
* https://design.activecollab.com/docs/foundations/icons
*/
var AccessLogIcon = /*#__PURE__*/React__default["default"].forwardRef(function (_ref, svgRef) {
var gradient = _ref.gradient,
props = _objectWithoutProperties(_ref, _excluded$4H);
return /*#__PURE__*/React__default["default"].createElement("svg", _extends({
width: 24,
height: 24,
xmlns: "http://www.w3.org/2000/svg",
"data-testid": "AccessLogIcon",
fill: gradient ? "url(#icon-gradient)" : props !== null && props !== void 0 && props.fill ? props.fill : "var(--color-theme-600)",
focusable: false,
ref: svgRef
}, props), /*#__PURE__*/React__default["default"].createElement(GradientDefs, {
gradient: gradient
}), /*#__PURE__*/React__default["default"].createElement("path", {
d: "M8.127 13.646a.5.5 0 01.146.354v3H9.37a.5.5 0 010 1H7.773a.5.5 0 01-.5-.5V14a.5.5 0 01.854-.354z"
}), /*#__PURE__*/React__default["default"].createElement("path", {
fillRule: "evenodd",
clipRule: "evenodd",
d: "M10.42 13.646a.5.5 0 01.353-.146h2a.5.5 0 01.5.5v3.5a.5.5 0 01-.5.5h-2a.5.5 0 01-.5-.5V14a.5.5 0 01.146-.354zm.853.854V17h1v-2.5h-1z"
}), /*#__PURE__*/React__default["default"].createElement("path", {
d: "M14.5 13.5a.5.5 0 00-.5.5v3.5a.5.5 0 00.5.5h2a.5.5 0 00.5-.5V16a.5.5 0 00-.5-.5H16a.5.5 0 000 1v.5h-1v-2.5h1.5a.5.5 0 000-1h-2z"
}), /*#__PURE__*/React__default["default"].createElement("path", {
fillRule: "evenodd",
clipRule: "evenodd",
d: "M5 2h11.03L21 7.075V20a2 2 0 01-2 2H5a2 2 0 01-2-2V4a2 2 0 012-2zm10.19 2H5v16h14V7.891L15.19 4z"
}));
});
AccessLogIcon.displayName = "AccessLogIcon";
var AccessLogIcon$1 = AccessLogIcon;
var _excluded$4G = ["gradient"];
/**
* @component ActivityIcon
* @description
*
* The React Icon component is a visual element that displays an icon to represent a concept, object, or action.
* The Icon component is
* customizable, allowing for variations in size, color, and style to fit the needs of the application.
*
* @prop {string} [gradient] - Optional CSS linear-gradient string to apply a custom fill.
* Format: "linear-gradient(<angle>deg, <color1>, <color2>, ...)"
*
* @example
* return (
* <ActivityIcon gradient="linear-gradient(135deg, #4da2ed, #f72222)" />
* )
*
* @example
* return (
* <ActivityIcon className="mr-2" />
* )
*
* @see
* https://system.activecollab.com/?path=/story/foundation-icons-icons--icons
* @see
* https://design.activecollab.com/docs/foundations/icons
*/
var ActivityIcon = /*#__PURE__*/React__default["default"].forwardRef(function (_ref, svgRef) {
var gradient = _ref.gradient,
props = _objectWithoutProperties(_ref, _excluded$4G);
return /*#__PURE__*/React__default["default"].createElement("svg", _extends({
width: 24,
height: 24,
viewBox: "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
"data-testid": "ActivityIcon",
fill: gradient ? "url(#icon-gradient)" : props !== null && props !== void 0