@taiv/ui 2.1.4 → 2.1.6

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 (31) hide show
  1. package/dist/components/Data/Cards/StatsCard/StatsCard.d.ts +3 -2
  2. package/dist/components/Data/Cards/StatsCard/StatsCard.d.ts.map +1 -1
  3. package/dist/components/Data/Cards/StatsCard/StatsCard.js +7 -3
  4. package/dist/components/Data/Cards/StatsCard/StatsCard.stories.d.ts +11 -0
  5. package/dist/components/Data/Cards/StatsCard/StatsCard.stories.d.ts.map +1 -0
  6. package/dist/components/Data/Cards/StatsCard/StatsCard.stories.js +57 -0
  7. package/dist/components/Info/Badge/Badge.d.ts +5 -1
  8. package/dist/components/Info/Badge/Badge.d.ts.map +1 -1
  9. package/dist/components/Info/Badge/Badge.js +8 -3
  10. package/dist/components/Info/Badge/Badge.stories.d.ts +1 -0
  11. package/dist/components/Info/Badge/Badge.stories.d.ts.map +1 -1
  12. package/dist/components/Info/Badge/Badge.stories.js +3 -0
  13. package/dist/components/Inputs/TextInputs/SearchBar/SearchBar.d.ts +4 -3
  14. package/dist/components/Inputs/TextInputs/SearchBar/SearchBar.d.ts.map +1 -1
  15. package/dist/components/Inputs/TextInputs/SearchBar/SearchBar.js +51 -11
  16. package/dist/components/Inputs/TextInputs/SearchBar/SearchBar.stories.d.ts +1 -0
  17. package/dist/components/Inputs/TextInputs/SearchBar/SearchBar.stories.d.ts.map +1 -1
  18. package/dist/components/Inputs/TextInputs/SearchBar/SearchBar.stories.js +22 -0
  19. package/dist/constants/colors.d.ts +28 -0
  20. package/dist/constants/colors.d.ts.map +1 -1
  21. package/dist/constants/colors.js +16 -0
  22. package/dist/hooks/index.d.ts +1 -1
  23. package/dist/hooks/index.d.ts.map +1 -1
  24. package/dist/hooks/index.js +1 -1
  25. package/dist/hooks/useMediaQuery.d.ts +1 -0
  26. package/dist/hooks/useMediaQuery.d.ts.map +1 -1
  27. package/dist/hooks/useMediaQuery.js +2 -1
  28. package/dist/utils/datetime.d.ts +13 -1
  29. package/dist/utils/datetime.d.ts.map +1 -1
  30. package/dist/utils/datetime.js +10 -1
  31. package/package.json +1 -1
@@ -3,7 +3,8 @@ import { CardProps } from '../../../Layout/Card/Card';
3
3
  import { primitives } from '../../../../constants/colors';
4
4
  import { formats, truncation } from '../../../../constants/data';
5
5
  export interface StatsCardProps extends Omit<CardProps, 'children'> {
6
- value: number;
6
+ value: number | string;
7
+ valueColor?: string;
7
8
  title: string;
8
9
  description?: string;
9
10
  format?: keyof typeof formats;
@@ -14,5 +15,5 @@ export interface StatsCardProps extends Omit<CardProps, 'children'> {
14
15
  increaseDescription?: string;
15
16
  truncateAt?: keyof typeof truncation;
16
17
  }
17
- export declare const StatsCard: ({ value, format, isDelta, title, description, increaseDescription, icon: Icon, iconColor, tooltip, truncateAt, ...cardProps }: StatsCardProps) => React.JSX.Element;
18
+ export declare const StatsCard: ({ value, valueColor, format, isDelta, title, description, increaseDescription, icon: Icon, iconColor, tooltip, truncateAt, ...cardProps }: StatsCardProps) => React.JSX.Element;
18
19
  //# sourceMappingURL=StatsCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StatsCard.d.ts","sourceRoot":"","sources":["../../../../../src/components/Data/Cards/StatsCard/StatsCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAQ,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAO5D,OAAO,EAAE,UAAU,EAAoB,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGjE,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,OAAO,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,OAAO,UAAU,CAAC;IACpC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,OAAO,UAAU,CAAC;CACtC;AAED,eAAO,MAAM,SAAS,GAAI,+HAA4J,cAAc,sBAmEnM,CAAC"}
1
+ {"version":3,"file":"StatsCard.d.ts","sourceRoot":"","sources":["../../../../../src/components/Data/Cards/StatsCard/StatsCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAQ,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAO5D,OAAO,EAAE,UAAU,EAAoB,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGjE,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACjE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,OAAO,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,OAAO,UAAU,CAAC;IACpC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,OAAO,UAAU,CAAC;CACtC;AAED,eAAO,MAAM,SAAS,GAAI,2IAAwK,cAAc,sBAuE/M,CAAC"}
@@ -9,9 +9,9 @@ import { IconBadge } from '../../../Misc/IconBadge/IconBadge';
9
9
  import { success, neutral } from '../../../../constants/colors';
10
10
  import { formats, truncation } from '../../../../constants/data';
11
11
  import { spacing } from '../../../../constants/spacing';
12
- export const StatsCard = ({ value, format = 'decimal', isDelta = false, title, description, increaseDescription, icon: Icon, iconColor = 'blue', tooltip, truncateAt, ...cardProps }) => {
12
+ export const StatsCard = ({ value, valueColor, format = 'decimal', isDelta = false, title, description, increaseDescription, icon: Icon, iconColor = 'blue', tooltip, truncateAt, ...cardProps }) => {
13
13
  const getDelta = () => {
14
- const currentDirection = isDelta ? (value > 0 ? 'positive' : value < 0 ? 'negative' : null) : null;
14
+ const currentDirection = isDelta && typeof value === 'number' ? (value > 0 ? 'positive' : value < 0 ? 'negative' : null) : null;
15
15
  return {
16
16
  direction: currentDirection,
17
17
  color: currentDirection === 'positive' ? success[200] : currentDirection === 'negative' ? neutral[200] : undefined,
@@ -20,6 +20,10 @@ export const StatsCard = ({ value, format = 'decimal', isDelta = false, title, d
20
20
  };
21
21
  const delta = getDelta();
22
22
  const formatValue = () => {
23
+ // String values are display text (e.g. a status word) — render verbatim,
24
+ // no number formatting, truncation, or delta prefix.
25
+ if (typeof value === 'string')
26
+ return value;
23
27
  const config = formats[format];
24
28
  let formattedNumber;
25
29
  if (truncateAt && value >= truncation[truncateAt].threshold) {
@@ -42,5 +46,5 @@ export const StatsCard = ({ value, format = 'decimal', isDelta = false, title, d
42
46
  result = `${config.prefix}${result}${config.suffix}`;
43
47
  return result;
44
48
  };
45
- return (_jsx(Card, { bg: neutral[25], animate: true, ...cardProps, children: _jsxs(Stack, { gap: spacing.lg, children: [_jsxs(Stack, { gap: spacing.xxs, children: [_jsxs(Group, { position: "apart", children: [_jsx(Title, { size: "3xl", weight: "bold", children: formatValue() }), Icon && _jsx(IconBadge, { icon: Icon, color: iconColor })] }), _jsxs(Group, { gap: spacing.sm, children: [_jsx(Title, { variant: "cardHeader", color: "#6D6D6D", weight: "medium", children: title }), tooltip && _jsx(InfoTooltip, { text: tooltip, maxWidth: "600px" })] })] }), _jsxs(Stack, { gap: "0", children: [description && _jsx(Text, { variant: "label", children: description }), delta.showIncrementalText && increaseDescription && (_jsx(Text, { variant: "label", color: delta.color, children: increaseDescription }))] })] }) }));
49
+ return (_jsx(Card, { bg: neutral[25], animate: true, ...cardProps, children: _jsxs(Stack, { gap: spacing.lg, children: [_jsxs(Stack, { gap: spacing.xxs, children: [_jsxs(Group, { position: "apart", children: [_jsx(Title, { size: "3xl", weight: "bold", color: valueColor, children: formatValue() }), Icon && _jsx(IconBadge, { icon: Icon, color: iconColor })] }), _jsxs(Group, { gap: spacing.sm, children: [_jsx(Title, { variant: "cardHeader", color: "#6D6D6D", weight: "medium", children: title }), tooltip && _jsx(InfoTooltip, { text: tooltip, maxWidth: "600px" })] })] }), _jsxs(Stack, { gap: "0", children: [description && _jsx(Text, { variant: "label", children: description }), delta.showIncrementalText && increaseDescription && (_jsx(Text, { variant: "label", color: delta.color, children: increaseDescription }))] })] }) }));
46
50
  };
@@ -0,0 +1,11 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { StatsCard } from './StatsCard';
3
+ declare const meta: Meta<typeof StatsCard>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof StatsCard>;
6
+ export declare const NumericValue: Story;
7
+ export declare const StringHealthy: Story;
8
+ export declare const StringIssuesDetected: Story;
9
+ export declare const StringNeedsAttention: Story;
10
+ export declare const StringNoDevices: Story;
11
+ //# sourceMappingURL=StatsCard.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatsCard.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Data/Cards/StatsCard/StatsCard.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,SAAS,CAUhC,CAAC;AACF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,SAAS,CAAC,CAAC;AAExC,eAAO,MAAM,YAAY,EAAE,KAO1B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAQ3B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KASlC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAUlC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAQ7B,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { IconDeviceTvFilled } from '@tabler/icons-react';
3
+ import { StatsCard } from './StatsCard';
4
+ import { error } from '../../../../constants/colors';
5
+ const meta = {
6
+ title: 'Components/Data/Cards/StatsCard',
7
+ component: StatsCard,
8
+ decorators: [
9
+ (Story) => (_jsx("div", { style: { width: '360px' }, children: _jsx(Story, {}) })),
10
+ ],
11
+ };
12
+ export default meta;
13
+ export const NumericValue = {
14
+ args: {
15
+ value: 42,
16
+ title: 'Items Active',
17
+ format: 'integer',
18
+ description: 'Across your rotation and active events',
19
+ },
20
+ };
21
+ export const StringHealthy = {
22
+ args: {
23
+ value: 'Healthy',
24
+ title: 'System Health',
25
+ description: 'All devices are operating as expected',
26
+ icon: _jsx(IconDeviceTvFilled, { size: 18 }),
27
+ iconColor: 'green',
28
+ },
29
+ };
30
+ export const StringIssuesDetected = {
31
+ args: {
32
+ value: 'Issues Detected',
33
+ title: 'System Health',
34
+ description: '2 of 5 devices have issues, your Taiv setup may not operate as expected',
35
+ icon: _jsx(IconDeviceTvFilled, { size: 18 }),
36
+ iconColor: 'yellow',
37
+ },
38
+ };
39
+ export const StringNeedsAttention = {
40
+ args: {
41
+ value: 'Needs Attention',
42
+ valueColor: error[200],
43
+ title: 'System Health',
44
+ description: '3 of 3 devices have issues, your Taiv setup may not operate as expected',
45
+ icon: _jsx(IconDeviceTvFilled, { size: 18 }),
46
+ iconColor: 'red',
47
+ },
48
+ };
49
+ export const StringNoDevices = {
50
+ args: {
51
+ value: 'No Devices',
52
+ title: 'System Health',
53
+ description: 'Add a device to get started with Taiv',
54
+ icon: _jsx(IconDeviceTvFilled, { size: 18 }),
55
+ iconColor: 'gray',
56
+ },
57
+ };
@@ -7,7 +7,11 @@ interface BadgeProps extends Omit<MantineBadgeProps, 'color' | 'leftSection'> {
7
7
  color?: BadgeColor;
8
8
  variant?: BadgeVariant;
9
9
  leftIcon?: React.ReactElement;
10
+ /** Shows click affordance (pointer cursor + hover feedback). Badge stays a
11
+ * display element — wrap it in a button (e.g. UnstyledButton) for the
12
+ * interaction itself. */
13
+ clickable?: boolean;
10
14
  }
11
- declare const Badge: ({ styles, color, variant, size, leftIcon, ...props }: BadgeProps) => React.JSX.Element;
15
+ declare const Badge: ({ styles, color, variant, size, leftIcon, clickable, ...props }: BadgeProps) => React.JSX.Element;
12
16
  export { Badge, type BadgeProps };
13
17
  //# sourceMappingURL=Badge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../../src/components/Info/Badge/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAyB,UAAU,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAKvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D,UAAU,UAAW,SAAQ,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,aAAa,CAAC;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC;CAC/B;AAED,QAAA,MAAM,KAAK,GAAI,sDAAqF,UAAU,sBAoC7G,CAAC;AAEF,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../../src/components/Info/Badge/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAyB,UAAU,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAKvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D,UAAU,UAAW,SAAQ,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,aAAa,CAAC;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC;IAC9B;;8BAE0B;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,QAAA,MAAM,KAAK,GAAI,iEAAwG,UAAU,sBAyChI,CAAC;AAEF,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,CAAC"}
@@ -4,25 +4,30 @@ import { Badge as MantineBadge } from '@mantine/core';
4
4
  import { fontBase } from '../../../constants/font';
5
5
  import { badgeSizes } from './sizes';
6
6
  import { getVariantStyles } from './variants';
7
- const Badge = ({ styles, color = 'primary', variant = 'outline', size = 'md', leftIcon, ...props }) => {
7
+ const Badge = ({ styles, color = 'primary', variant = 'outline', size = 'md', leftIcon, clickable = false, ...props }) => {
8
8
  const selectedSize = badgeSizes[size];
9
9
  const variantStyles = getVariantStyles(color);
10
10
  const selectedVariant = variantStyles[variant];
11
+ const cursor = clickable ? 'pointer' : 'default';
11
12
  const style = {
12
13
  root: {
13
14
  padding: selectedSize.padding,
14
15
  height: selectedSize.height,
15
- cursor: 'default',
16
+ cursor,
16
17
  display: 'flex',
17
18
  alignItems: 'center',
18
19
  justifyContent: 'center',
19
20
  ...selectedVariant,
21
+ ...(clickable && {
22
+ transition: 'filter 120ms ease',
23
+ '&:hover': { filter: 'brightness(0.96)' },
24
+ }),
20
25
  },
21
26
  inner: {
22
27
  ...fontBase,
23
28
  ...selectedSize.fontSize,
24
29
  textTransform: 'none',
25
- cursor: 'default',
30
+ cursor,
26
31
  },
27
32
  leftSection: {
28
33
  display: 'flex',
@@ -11,5 +11,6 @@ export declare const Default: Story;
11
11
  export declare const Variants: Story;
12
12
  export declare const Colors: Story;
13
13
  export declare const Sizes: Story;
14
+ export declare const Clickable: Story;
14
15
  export declare const WithLeftIcon: Story;
15
16
  //# sourceMappingURL=Badge.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/Info/Badge/Badge.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAK1C,KAAK,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,kBAAkB,CAAC;AAEhE,KAAK,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;IACnD,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,CAAC;AAEF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,cAAc,CAwE9B,CAAC;AAQF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;AAEtC,eAAO,MAAM,OAAO,EAAE,KAYrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAStB,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,KAUpB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAQnB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAsC1B,CAAC"}
1
+ {"version":3,"file":"Badge.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/Info/Badge/Badge.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAK1C,KAAK,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,kBAAkB,CAAC;AAEhE,KAAK,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;IACnD,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,CAAC;AAEF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,cAAc,CAwE9B,CAAC;AAQF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;AAEtC,eAAO,MAAM,OAAO,EAAE,KAYrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAStB,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,KAUpB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAQnB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAcvB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAsC1B,CAAC"}
@@ -103,6 +103,9 @@ export const Colors = {
103
103
  export const Sizes = {
104
104
  render: () => (_jsxs(Group, { gap: spacing.lg, align: "flex-end", children: [_jsx(Badge, { size: "sm", color: "primary", children: "Small" }), _jsx(Badge, { size: "md", color: "primary", children: "Medium" }), _jsx(Badge, { size: "lg", color: "primary", children: "Large" })] })),
105
105
  };
106
+ export const Clickable = {
107
+ render: () => (_jsxs(Group, { gap: spacing.lg, children: [_jsx(Badge, { clickable: true, variant: "filled", color: "success", leftIcon: _jsx(IconCircleFilled, {}), children: "Healthy" }), _jsx(Badge, { clickable: true, variant: "filled", color: "error", leftIcon: _jsx(IconCircleFilled, {}), children: "Offline" }), _jsx(Badge, { variant: "filled", color: "neutral", children: "Not clickable" })] })),
108
+ };
106
109
  export const WithLeftIcon = {
107
110
  render: () => (_jsxs(Stack, { gap: spacing.lg, children: [_jsxs(Group, { gap: spacing.lg, children: [_jsx(Badge, { size: "sm", leftIcon: _jsx(IconCheck, {}), color: "success", children: "Completed" }), _jsx(Badge, { size: "sm", variant: "filled", leftIcon: _jsx(IconCircleFilled, {}), color: "success", children: "Online" }), _jsx(Badge, { size: "sm", variant: "gradient", leftIcon: _jsx(IconCheck, {}), color: "success", children: "In Progress" })] }), _jsxs(Group, { gap: spacing.lg, children: [_jsx(Badge, { leftIcon: _jsx(IconCheck, {}), color: "success", children: "Completed" }), _jsx(Badge, { variant: "filled", leftIcon: _jsx(IconCircleFilled, {}), color: "success", children: "Online" }), _jsx(Badge, { variant: "gradient", leftIcon: _jsx(IconCheck, {}), color: "success", children: "In Progress" })] }), _jsxs(Group, { gap: spacing.lg, children: [_jsx(Badge, { size: "lg", leftIcon: _jsx(IconCheck, {}), color: "success", children: "Completed" }), _jsx(Badge, { size: "lg", variant: "filled", leftIcon: _jsx(IconCircleFilled, {}), color: "success", children: "Online" }), _jsx(Badge, { size: "lg", variant: "gradient", leftIcon: _jsx(IconCheck, {}), color: "success", children: "In Progress" })] })] })),
108
111
  };
@@ -1,8 +1,9 @@
1
- import React from 'react';
2
- import { TextInputProps } from '@mantine/core';
1
+ import type { TextInputProps } from '@mantine/core';
2
+ import type React from 'react';
3
3
  interface SearchBarProps extends TextInputProps {
4
4
  fullWidth?: boolean;
5
+ useHotkey?: boolean;
5
6
  }
6
- declare const SearchBar: ({ width, fullWidth, size, styles, ...props }: SearchBarProps) => React.JSX.Element;
7
+ declare const SearchBar: ({ width, fullWidth, size, styles, useHotkey, id, onKeyDown, value, ...props }: SearchBarProps) => React.JSX.Element;
7
8
  export { SearchBar };
8
9
  //# sourceMappingURL=SearchBar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../../../../src/components/Inputs/TextInputs/SearchBar/SearchBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAO/C,UAAU,cAAe,SAAQ,cAAc;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,QAAA,MAAM,SAAS,GAAI,8CAA6D,cAAc,sBAmC7F,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../../../../src/components/Inputs/TextInputs/SearchBar/SearchBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,UAAU,cAAe,SAAQ,cAAc;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,QAAA,MAAM,SAAS,GAAI,+EAUhB,cAAc,sBAkGhB,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -1,31 +1,71 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { TextInput } from '../TextInput/TextInput';
2
+ import { useHotkeys, useId } from '@mantine/hooks';
3
3
  import { neutral, red } from '../../../../constants/colors';
4
4
  import { fontBase } from '../../../../constants/font';
5
5
  import { spacing } from '../../../../constants/spacing';
6
+ import { useIsMac, useTablet, useTouchDevice } from '../../../../hooks';
7
+ import { Text } from '../../../Typography/Text/Text';
6
8
  import { componentSizes } from '../shared/sizes';
7
- const SearchBar = ({ width, fullWidth = false, size = 'md', styles, ...props }) => {
9
+ import { TextInput } from '../TextInput/TextInput';
10
+ const SearchBar = ({ width, fullWidth = false, size = 'md', styles, useHotkey = false, id, onKeyDown, value, ...props }) => {
8
11
  const selectedSize = componentSizes[size || 'md'];
9
12
  const computedWidth = fullWidth ? '100%' : width || `${selectedSize.width}px`;
13
+ const inputId = useId(id);
14
+ const isTablet = useTablet();
15
+ const isTouchDevice = useTouchDevice();
16
+ const isMobile = isTablet || isTouchDevice;
17
+ const isMac = useIsMac();
18
+ const hotkeyEnabled = useHotkey && !isMobile;
19
+ const hasValue = Boolean(value);
20
+ useHotkeys(hotkeyEnabled
21
+ ? [
22
+ [
23
+ 'mod+K',
24
+ (event) => {
25
+ event.preventDefault();
26
+ const el = document.getElementById(inputId);
27
+ el === null || el === void 0 ? void 0 : el.focus();
28
+ el === null || el === void 0 ? void 0 : el.select();
29
+ },
30
+ ],
31
+ ]
32
+ : [], []);
33
+ const handleKeyDown = (event) => {
34
+ if (hotkeyEnabled && event.key === 'Escape')
35
+ event.currentTarget.blur();
36
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
37
+ };
10
38
  const style = {
11
39
  input: {
12
40
  ...fontBase,
13
- fontSize: selectedSize.fontSize,
14
- color: neutral[200],
15
- border: `1px solid ${neutral[100]}`,
16
- borderRadius: '8px',
17
- height: `${selectedSize.height}px`,
18
- transition: 'all 200ms ease-in-out',
19
41
  '&[data-invalid]': {
20
- borderColor: red[200],
21
- color: neutral[200],
22
42
  '&::placeholder': {
23
43
  color: red[200],
24
44
  },
45
+ borderColor: red[200],
46
+ color: neutral[200],
25
47
  },
48
+ border: `1px solid ${neutral[100]}`,
49
+ borderRadius: '8px',
50
+ color: neutral[200],
51
+ fontSize: selectedSize.fontSize,
52
+ height: `${selectedSize.height}px`,
53
+ transition: 'all 200ms ease-in-out',
26
54
  },
55
+ ...(hotkeyEnabled
56
+ ? { rightSection: { justifyContent: 'flex-end', paddingRight: spacing.md } }
57
+ : {}),
27
58
  ...styles,
28
59
  };
29
- return (_jsx(TextInput, { placeholder: "Search", width: computedWidth, styles: style, icon: _jsx("i", { className: "fas fa-search", style: { fontSize: 14, marginLeft: spacing.xxs } }), size: 'lg', fullWidth: fullWidth, ...props }));
60
+ const hotkeyHint = hotkeyEnabled && !hasValue ? (_jsx(Text, { span: true, variant: 'label', color: '#adb5bd', sx: {
61
+ alignItems: 'center',
62
+ display: 'inline-flex',
63
+ letterSpacing: 0.5,
64
+ lineHeight: 1,
65
+ pointerEvents: 'none',
66
+ userSelect: 'none',
67
+ whiteSpace: 'nowrap',
68
+ }, children: isMac ? '⌘+K' : 'Ctrl+K' })) : undefined;
69
+ return (_jsx(TextInput, { fullWidth: fullWidth, icon: _jsx("i", { className: 'fas fa-search', style: { fontSize: 14, marginLeft: spacing.xxs } }), id: inputId, onKeyDown: handleKeyDown, placeholder: 'Search', rightSection: hotkeyHint, rightSectionWidth: hotkeyEnabled ? (isMac ? 50 : 66) : undefined, size: 'lg', styles: style, value: value, width: computedWidth, ...props }));
30
70
  };
31
71
  export { SearchBar };
@@ -4,6 +4,7 @@ declare const meta: Meta<typeof SearchBar>;
4
4
  export default meta;
5
5
  type Story = StoryObj<typeof meta>;
6
6
  export declare const Default: Story;
7
+ export declare const UseHotkey: Story;
7
8
  export declare const Variants: Story;
8
9
  export declare const Sizes: Story;
9
10
  export declare const States: Story;
@@ -1 +1 @@
1
- {"version":3,"file":"SearchBar.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Inputs/TextInputs/SearchBar/SearchBar.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,SAAS,CA2FhC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAiBrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAetB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAgBnB,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,KAiBpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,KA2BrC,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KA6C3B,CAAC"}
1
+ {"version":3,"file":"SearchBar.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Inputs/TextInputs/SearchBar/SearchBar.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,SAAS,CAmGhC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAiBrB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAavB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAetB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAgBnB,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,KAiBpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,KA2BrC,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KA6C3B,CAAC"}
@@ -70,6 +70,14 @@ const meta = {
70
70
  defaultValue: { summary: 'false' },
71
71
  },
72
72
  },
73
+ useHotkey: {
74
+ control: { type: 'boolean' },
75
+ description: '⌘K / Ctrl+K focuses this input from anywhere; shows a hint badge. Enable on one SearchBar per page.',
76
+ table: {
77
+ type: { summary: 'boolean' },
78
+ defaultValue: { summary: 'false' },
79
+ },
80
+ },
73
81
  width: {
74
82
  control: { type: 'number' },
75
83
  description: 'Custom width in pixels',
@@ -112,6 +120,20 @@ export const Default = {
112
120
  },
113
121
  },
114
122
  };
123
+ export const UseHotkey = {
124
+ args: {
125
+ useHotkey: true,
126
+ fullWidth: false,
127
+ placeholder: 'Search',
128
+ },
129
+ parameters: {
130
+ docs: {
131
+ source: {
132
+ code: `<SearchBar useHotkey value={query} onChange={handleSearch} />`,
133
+ },
134
+ },
135
+ },
136
+ };
115
137
  export const Variants = {
116
138
  render: () => (_jsxs(Group, { gap: "20px", children: [_jsx(SearchBar, { label: "Find Users", description: "Search by name, email, or username" }), _jsx(SearchBar, { placeholder: "Search products..." }), _jsx(SearchBar, { label: "Product Search", error: "Search query is too short" })] })),
117
139
  parameters: {
@@ -50,6 +50,20 @@ export declare const salmon: {
50
50
  readonly 100: "#FFD1C7";
51
51
  readonly 200: "#FF806F";
52
52
  };
53
+ export declare const pink: {
54
+ readonly 25: "#FFEBF5";
55
+ readonly 50: "#FFC2E0";
56
+ readonly 100: "#FF94CB";
57
+ readonly 200: "#FF69B4";
58
+ readonly 300: "#AB23B0";
59
+ };
60
+ export declare const brown: {
61
+ readonly 25: "#F9F1E7";
62
+ readonly 50: "#EBD3B3";
63
+ readonly 100: "#CE9752";
64
+ readonly 200: "#A55B00";
65
+ readonly 300: "#6E3D00";
66
+ };
53
67
  export declare const teal: {
54
68
  readonly 25: "#E0F2F1";
55
69
  readonly 50: "#E0F2F1";
@@ -141,6 +155,13 @@ export declare const primitives: {
141
155
  readonly 200: "#00A6F4";
142
156
  readonly 300: "#0081CE";
143
157
  };
158
+ readonly brown: {
159
+ readonly 25: "#F9F1E7";
160
+ readonly 50: "#EBD3B3";
161
+ readonly 100: "#CE9752";
162
+ readonly 200: "#A55B00";
163
+ readonly 300: "#6E3D00";
164
+ };
144
165
  readonly gray: {
145
166
  readonly 25: "#F8FAFC";
146
167
  readonly 50: "#EDEDED";
@@ -157,6 +178,13 @@ export declare const primitives: {
157
178
  readonly 200: "#00DE6F";
158
179
  readonly 300: "#00A84F";
159
180
  };
181
+ readonly pink: {
182
+ readonly 25: "#FFEBF5";
183
+ readonly 50: "#FFC2E0";
184
+ readonly 100: "#FF94CB";
185
+ readonly 200: "#FF69B4";
186
+ readonly 300: "#AB23B0";
187
+ };
160
188
  readonly purple: {
161
189
  readonly 25: "#F3EEFF";
162
190
  readonly 50: "#C4A8FF";
@@ -1 +1 @@
1
- {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/constants/colors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,YAAY,CAAC;AAC/B,eAAO,MAAM,KAAK,YAAY,CAAC;AAE/B,eAAO,MAAM,IAAI;;;;;;;;CAQP,CAAC;AAEX,eAAO,MAAM,IAAI;;;;;;CAMP,CAAC;AAEX,eAAO,MAAM,KAAK;;;;;;CAMR,CAAC;AAGX,eAAO,MAAM,MAAM;;;;;;CAMT,CAAC;AAEX,eAAO,MAAM,GAAG;;;;;;CAMN,CAAC;AAGX,eAAO,MAAM,MAAM;;;;;;CAMT,CAAC;AAEX,eAAO,MAAM,MAAM;;;;;CAKT,CAAC;AAEX,eAAO,MAAM,IAAI;;;;;CAKP,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;CAAS,CAAC;AAC9B,eAAO,MAAM,KAAK;;;;;;CAAM,CAAC;AACzB,eAAO,MAAM,OAAO;;;;;;CAAQ,CAAC;AAC7B,eAAO,MAAM,OAAO;;;;;;CAAS,CAAC;AAC9B,eAAO,MAAM,OAAO;;;;;;;;CAAO,CAAC;AAE5B,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOT,CAAC;AAGX,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASb,CAAC"}
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/constants/colors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,YAAY,CAAC;AAC/B,eAAO,MAAM,KAAK,YAAY,CAAC;AAE/B,eAAO,MAAM,IAAI;;;;;;;;CAQP,CAAC;AAEX,eAAO,MAAM,IAAI;;;;;;CAMP,CAAC;AAEX,eAAO,MAAM,KAAK;;;;;;CAMR,CAAC;AAGX,eAAO,MAAM,MAAM;;;;;;CAMT,CAAC;AAEX,eAAO,MAAM,GAAG;;;;;;CAMN,CAAC;AAGX,eAAO,MAAM,MAAM;;;;;;CAMT,CAAC;AAEX,eAAO,MAAM,MAAM;;;;;CAKT,CAAC;AAEX,eAAO,MAAM,IAAI;;;;;;CAMP,CAAC;AAEX,eAAO,MAAM,KAAK;;;;;;CAMR,CAAC;AAEX,eAAO,MAAM,IAAI;;;;;CAKP,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;CAAS,CAAC;AAC9B,eAAO,MAAM,KAAK;;;;;;CAAM,CAAC;AACzB,eAAO,MAAM,OAAO;;;;;;CAAQ,CAAC;AAC7B,eAAO,MAAM,OAAO;;;;;;CAAS,CAAC;AAC9B,eAAO,MAAM,OAAO;;;;;;;;CAAO,CAAC;AAE5B,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOT,CAAC;AAGX,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWb,CAAC"}
@@ -52,6 +52,20 @@ export const salmon = {
52
52
  100: "#FFD1C7",
53
53
  200: "#FF806F",
54
54
  };
55
+ export const pink = {
56
+ 25: "#FFEBF5",
57
+ 50: "#FFC2E0",
58
+ 100: "#FF94CB",
59
+ 200: "#FF69B4",
60
+ 300: "#AB23B0",
61
+ };
62
+ export const brown = {
63
+ 25: "#F9F1E7",
64
+ 50: "#EBD3B3",
65
+ 100: "#CE9752",
66
+ 200: "#A55B00",
67
+ 300: "#6E3D00",
68
+ };
55
69
  export const teal = {
56
70
  25: "#E0F2F1",
57
71
  50: "#E0F2F1",
@@ -75,8 +89,10 @@ export const colors = {
75
89
  // Primitive color exports - for purely aesthetic purpose, i.e. icons, backgrounds, etc. anything that we want to hardcode to a specific color
76
90
  export const primitives = {
77
91
  blue,
92
+ brown,
78
93
  gray,
79
94
  green,
95
+ pink,
80
96
  purple,
81
97
  red,
82
98
  salmon,
@@ -5,7 +5,7 @@ export { useScrollLock } from './useScrollLock';
5
5
  export { useElementSize, useIntersection, useListState, useResizeObserver, useTimeout, useDebouncedValue } from '@mantine/hooks';
6
6
  export { useForm } from '@mantine/form';
7
7
  export type { UseFormReturnType } from '@mantine/form';
8
- export { useMediaQuery, useScreenSize, useMobile, useTablet, useLaptop, useDesktop, useWide, useTouchDevice } from './useMediaQuery';
8
+ export { useMediaQuery, useScreenSize, useMobile, useTablet, useLaptop, useDesktop, useWide, useTouchDevice, useIsMac } from './useMediaQuery';
9
9
  export { useNotifications } from './useNotifications';
10
10
  export { useConfirmationModal } from './useConfirmationModal';
11
11
  export { useInfoModal } from './useInfoModal';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACjI,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACrI,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACjI,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC/I,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -4,7 +4,7 @@ export { useHeadroom } from './useHeadroom';
4
4
  export { useScrollLock } from './useScrollLock';
5
5
  export { useElementSize, useIntersection, useListState, useResizeObserver, useTimeout, useDebouncedValue } from '@mantine/hooks';
6
6
  export { useForm } from '@mantine/form';
7
- export { useMediaQuery, useScreenSize, useMobile, useTablet, useLaptop, useDesktop, useWide, useTouchDevice } from './useMediaQuery';
7
+ export { useMediaQuery, useScreenSize, useMobile, useTablet, useLaptop, useDesktop, useWide, useTouchDevice, useIsMac } from './useMediaQuery';
8
8
  export { useNotifications } from './useNotifications';
9
9
  export { useConfirmationModal } from './useConfirmationModal';
10
10
  export { useInfoModal } from './useInfoModal';
@@ -8,4 +8,5 @@ export declare const useLaptop: () => boolean;
8
8
  export declare const useDesktop: () => boolean;
9
9
  export declare const useWide: () => boolean;
10
10
  export declare const useTouchDevice: () => boolean;
11
+ export declare const useIsMac: () => boolean;
11
12
  //# sourceMappingURL=useMediaQuery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMediaQuery.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaQuery.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,eAAO,MAAM,aAAa,6BAAuB,CAAC;AAElD,eAAO,MAAM,aAAa,QAAO,MAAM,OAAO,WAW7C,CAAC;AAEF,eAAO,MAAM,SAAS,eAA8D,CAAC;AACrF,eAAO,MAAM,SAAS,eAA8D,CAAC;AACrF,eAAO,MAAM,SAAS,eAA8D,CAAC;AACrF,eAAO,MAAM,UAAU,eAA+D,CAAC;AACvF,eAAO,MAAM,OAAO,eAA4D,CAAC;AACjF,eAAO,MAAM,cAAc,eAA2C,CAAC"}
1
+ {"version":3,"file":"useMediaQuery.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaQuery.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,oBAAoB,EAAS,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,eAAO,MAAM,aAAa,6BAAuB,CAAC;AAElD,eAAO,MAAM,aAAa,QAAO,MAAM,OAAO,WAW7C,CAAC;AAEF,eAAO,MAAM,SAAS,eAA8D,CAAC;AACrF,eAAO,MAAM,SAAS,eAA8D,CAAC;AACrF,eAAO,MAAM,SAAS,eAA8D,CAAC;AACrF,eAAO,MAAM,UAAU,eAA+D,CAAC;AACvF,eAAO,MAAM,OAAO,eAA4D,CAAC;AACjF,eAAO,MAAM,cAAc,eAA2C,CAAC;AAEvE,eAAO,MAAM,QAAQ,eAA4B,CAAC"}
@@ -1,4 +1,4 @@
1
- import { useMediaQuery as useMantineMediaQuery } from '@mantine/hooks';
1
+ import { useMediaQuery as useMantineMediaQuery, useOs } from '@mantine/hooks';
2
2
  import { breakpoints } from '../constants/breakpoints';
3
3
  export const useMediaQuery = useMantineMediaQuery;
4
4
  export const useScreenSize = () => {
@@ -22,3 +22,4 @@ export const useLaptop = () => useMediaQuery(`(max-width: ${breakpoints.LAPTOP}p
22
22
  export const useDesktop = () => useMediaQuery(`(max-width: ${breakpoints.DESKTOP}px)`);
23
23
  export const useWide = () => useMediaQuery(`(max-width: ${breakpoints.WIDE}px)`);
24
24
  export const useTouchDevice = () => useMediaQuery('(pointer: coarse)');
25
+ export const useIsMac = () => useOs() === 'macos';
@@ -1,3 +1,11 @@
1
+ interface FormatRelativeTimeOptions {
2
+ /**
3
+ * Always return exact two-unit values ('5h 12m ago', '3d 4h ago') instead
4
+ * of collapsing to coarse tiers ('3w ago'). For dashboards/ops surfaces
5
+ * where "a while ago" isn't good enough.
6
+ */
7
+ precise?: boolean;
8
+ }
1
9
  /**
2
10
  * Compact "time-since" string with minute/hour granularity. Past-only —
3
11
  * use this for "edited 5m ago", "uploaded 12h ago" style metadata where
@@ -8,11 +16,15 @@
8
16
  * @param reference The "now" to measure against. Defaults to the current
9
17
  * time; pass an explicit value to make output deterministic
10
18
  * in tests.
19
+ * @param options `{ precise: true }` keeps exact two-unit output at every
20
+ * magnitude instead of the coarse w/mo/y tiers.
11
21
  *
12
22
  * @example
13
23
  * formatRelativeTime(thirtySecondsAgo) // 'just now'
14
24
  * formatRelativeTime(twelveHoursAgo) // '12h ago'
15
25
  * formatRelativeTime(threeWeeksAgo) // '3w ago'
26
+ * formatRelativeTime(threeWeeksAgo, undefined, { precise: true }) // '21d 4h ago'
16
27
  */
17
- export declare const formatRelativeTime: (input: string | number | Date, reference?: string | number | Date) => string;
28
+ export declare const formatRelativeTime: (input: string | number | Date, reference?: string | number | Date, options?: FormatRelativeTimeOptions) => string;
29
+ export {};
18
30
  //# sourceMappingURL=datetime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../../src/utils/datetime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,GAAG,MAAM,GAAG,IAAI,EAC7B,YAAW,MAAM,GAAG,MAAM,GAAG,IAAiB,KAC7C,MAqBF,CAAC"}
1
+ {"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../../src/utils/datetime.ts"],"names":[],"mappings":"AAAA,UAAU,yBAAyB;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,GAAG,MAAM,GAAG,IAAI,EAC7B,YAAW,MAAM,GAAG,MAAM,GAAG,IAAiB,EAC9C,UAAS,yBAA8B,KACtC,MA4BF,CAAC"}
@@ -8,13 +8,16 @@
8
8
  * @param reference The "now" to measure against. Defaults to the current
9
9
  * time; pass an explicit value to make output deterministic
10
10
  * in tests.
11
+ * @param options `{ precise: true }` keeps exact two-unit output at every
12
+ * magnitude instead of the coarse w/mo/y tiers.
11
13
  *
12
14
  * @example
13
15
  * formatRelativeTime(thirtySecondsAgo) // 'just now'
14
16
  * formatRelativeTime(twelveHoursAgo) // '12h ago'
15
17
  * formatRelativeTime(threeWeeksAgo) // '3w ago'
18
+ * formatRelativeTime(threeWeeksAgo, undefined, { precise: true }) // '21d 4h ago'
16
19
  */
17
- export const formatRelativeTime = (input, reference = new Date()) => {
20
+ export const formatRelativeTime = (input, reference = new Date(), options = {}) => {
18
21
  const targetMs = input instanceof Date ? input.getTime() : new Date(input).getTime();
19
22
  const refMs = reference instanceof Date ? reference.getTime() : new Date(reference).getTime();
20
23
  if (Number.isNaN(targetMs) || Number.isNaN(refMs))
@@ -26,6 +29,12 @@ export const formatRelativeTime = (input, reference = new Date()) => {
26
29
  if (minutes < 60)
27
30
  return `${minutes}m ago`;
28
31
  const hours = Math.floor(minutes / 60);
32
+ if (options.precise) {
33
+ if (hours < 48)
34
+ return `${hours}h ${minutes % 60}m ago`;
35
+ const days = Math.floor(hours / 24);
36
+ return `${days}d ${hours % 24}h ago`;
37
+ }
29
38
  if (hours < 24)
30
39
  return `${hours}h ago`;
31
40
  const days = Math.floor(hours / 24);
package/package.json CHANGED
@@ -85,5 +85,5 @@
85
85
  "storybook": "storybook dev -p 6006"
86
86
  },
87
87
  "types": "dist/index.d.ts",
88
- "version": "2.1.4"
88
+ "version": "2.1.6"
89
89
  }