@wheelhouse/ui 0.2.3 → 0.2.5

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 (118) hide show
  1. package/dist/blocks/columns/columns-types.d.ts +25 -1
  2. package/dist/blocks/columns/columns-types.d.ts.map +1 -1
  3. package/dist/blocks/columns/columns-types.js +4 -0
  4. package/dist/blocks/columns/columns-utils.d.ts +31 -1
  5. package/dist/blocks/columns/columns-utils.d.ts.map +1 -1
  6. package/dist/blocks/columns/columns-utils.js +86 -1
  7. package/dist/blocks/columns/columns.d.ts +1 -1
  8. package/dist/blocks/columns/columns.d.ts.map +1 -1
  9. package/dist/blocks/columns/columns.js +81 -20
  10. package/dist/blocks/columns/columns.stories.d.ts +1 -0
  11. package/dist/blocks/columns/columns.stories.d.ts.map +1 -1
  12. package/dist/blocks/columns/columns.stories.js +21 -1
  13. package/dist/blocks/columns/index.d.ts +1 -1
  14. package/dist/blocks/columns/index.d.ts.map +1 -1
  15. package/dist/blocks/floating-menu-widget/floating-menu-widget.d.ts +26 -0
  16. package/dist/blocks/floating-menu-widget/floating-menu-widget.d.ts.map +1 -0
  17. package/dist/blocks/floating-menu-widget/floating-menu-widget.js +200 -0
  18. package/dist/blocks/floating-menu-widget/floating-menu-widget.stories.d.ts +15 -0
  19. package/dist/blocks/floating-menu-widget/floating-menu-widget.stories.d.ts.map +1 -0
  20. package/dist/blocks/floating-menu-widget/floating-menu-widget.stories.js +22 -0
  21. package/dist/blocks/floating-menu-widget/index.d.ts +3 -0
  22. package/dist/blocks/floating-menu-widget/index.d.ts.map +1 -0
  23. package/dist/blocks/floating-menu-widget/index.js +1 -0
  24. package/dist/blocks/index.d.ts +1 -0
  25. package/dist/blocks/index.d.ts.map +1 -1
  26. package/dist/blocks/index.js +1 -0
  27. package/dist/components/button/button.d.ts +18 -11
  28. package/dist/components/button/button.d.ts.map +1 -1
  29. package/dist/components/button/button.js +27 -14
  30. package/dist/components/button/button.stories.d.ts +11 -0
  31. package/dist/components/button/button.stories.d.ts.map +1 -1
  32. package/dist/components/button/button.stories.js +84 -0
  33. package/dist/components/data-grid/data-grid-column-filter.d.ts +15 -0
  34. package/dist/components/data-grid/data-grid-column-filter.d.ts.map +1 -0
  35. package/dist/components/data-grid/data-grid-column-filter.js +36 -0
  36. package/dist/components/data-grid/data-grid-column-header.d.ts +15 -0
  37. package/dist/components/data-grid/data-grid-column-header.d.ts.map +1 -0
  38. package/dist/components/data-grid/data-grid-column-header.js +137 -0
  39. package/dist/components/data-grid/data-grid-column-visibility.d.ts +8 -0
  40. package/dist/components/data-grid/data-grid-column-visibility.d.ts.map +1 -0
  41. package/dist/components/data-grid/data-grid-column-visibility.js +13 -0
  42. package/dist/components/data-grid/data-grid-pagination.d.ts +20 -0
  43. package/dist/components/data-grid/data-grid-pagination.d.ts.map +1 -0
  44. package/dist/components/data-grid/data-grid-pagination.js +76 -0
  45. package/dist/components/data-grid/data-grid-scroll-area.d.ts +11 -0
  46. package/dist/components/data-grid/data-grid-scroll-area.d.ts.map +1 -0
  47. package/dist/components/data-grid/data-grid-scroll-area.js +218 -0
  48. package/dist/components/data-grid/data-grid-table-dnd-rows.d.ts +12 -0
  49. package/dist/components/data-grid/data-grid-table-dnd-rows.d.ts.map +1 -0
  50. package/dist/components/data-grid/data-grid-table-dnd-rows.js +91 -0
  51. package/dist/components/data-grid/data-grid-table-dnd.d.ts +8 -0
  52. package/dist/components/data-grid/data-grid-table-dnd.d.ts.map +1 -0
  53. package/dist/components/data-grid/data-grid-table-dnd.js +95 -0
  54. package/dist/components/data-grid/data-grid-table-virtual.d.ts +28 -0
  55. package/dist/components/data-grid/data-grid-table-virtual.d.ts.map +1 -0
  56. package/dist/components/data-grid/data-grid-table-virtual.js +133 -0
  57. package/dist/components/data-grid/data-grid-table.d.ts +98 -0
  58. package/dist/components/data-grid/data-grid-table.d.ts.map +1 -0
  59. package/dist/components/data-grid/data-grid-table.js +560 -0
  60. package/dist/components/data-grid/data-grid.d.ts +94 -0
  61. package/dist/components/data-grid/data-grid.d.ts.map +1 -0
  62. package/dist/components/data-grid/data-grid.js +123 -0
  63. package/dist/components/data-grid/data-grid.stories.d.ts +14 -0
  64. package/dist/components/data-grid/data-grid.stories.d.ts.map +1 -0
  65. package/dist/components/data-grid/data-grid.stories.js +47 -0
  66. package/dist/components/data-grid/index.d.ts +14 -0
  67. package/dist/components/data-grid/index.d.ts.map +1 -0
  68. package/dist/components/data-grid/index.js +10 -0
  69. package/dist/components/filters/filter-date-metric-value.d.ts.map +1 -1
  70. package/dist/components/filters/filter-date-metric-value.js +83 -8
  71. package/dist/components/filters/filter-fields-listing-demo.d.ts +12 -0
  72. package/dist/components/filters/filter-fields-listing-demo.d.ts.map +1 -0
  73. package/dist/components/filters/filter-fields-listing-demo.js +565 -0
  74. package/dist/components/filters/filters-types.d.ts +7 -0
  75. package/dist/components/filters/filters-types.d.ts.map +1 -1
  76. package/dist/components/filters/filters.stories.d.ts.map +1 -1
  77. package/dist/components/filters/filters.stories.js +8 -149
  78. package/dist/components/filters/index.d.ts +1 -0
  79. package/dist/components/filters/index.d.ts.map +1 -1
  80. package/dist/components/filters/index.js +1 -0
  81. package/dist/components/index.d.ts +3 -0
  82. package/dist/components/index.d.ts.map +1 -1
  83. package/dist/components/index.js +3 -0
  84. package/dist/components/popover/index.d.ts +1 -0
  85. package/dist/components/popover/index.d.ts.map +1 -1
  86. package/dist/components/popover/index.js +1 -0
  87. package/dist/components/popover/popover-handle.d.ts +6 -0
  88. package/dist/components/popover/popover-handle.d.ts.map +1 -0
  89. package/dist/components/popover/popover-handle.js +6 -0
  90. package/dist/components/popover/popover.d.ts +41 -7
  91. package/dist/components/popover/popover.d.ts.map +1 -1
  92. package/dist/components/popover/popover.js +50 -3
  93. package/dist/components/progress/progress.js +1 -1
  94. package/dist/components/progress/progress.stories.d.ts +11 -2
  95. package/dist/components/progress/progress.stories.d.ts.map +1 -1
  96. package/dist/components/progress/progress.stories.js +77 -4
  97. package/dist/components/sidebar/index.d.ts +2 -0
  98. package/dist/components/sidebar/index.d.ts.map +1 -0
  99. package/dist/components/sidebar/index.js +1 -0
  100. package/dist/components/sidebar/sidebar.d.ts +64 -0
  101. package/dist/components/sidebar/sidebar.d.ts.map +1 -0
  102. package/dist/components/sidebar/sidebar.js +255 -0
  103. package/dist/components/sidebar/sidebar.stories.d.ts +20 -0
  104. package/dist/components/sidebar/sidebar.stories.d.ts.map +1 -0
  105. package/dist/components/sidebar/sidebar.stories.js +181 -0
  106. package/dist/components/skeleton/index.d.ts +3 -0
  107. package/dist/components/skeleton/index.d.ts.map +1 -0
  108. package/dist/components/skeleton/index.js +1 -0
  109. package/dist/components/skeleton/skeleton.d.ts +7 -0
  110. package/dist/components/skeleton/skeleton.d.ts.map +1 -0
  111. package/dist/components/skeleton/skeleton.js +8 -0
  112. package/dist/components/sortable/sortable.d.ts +4 -2
  113. package/dist/components/sortable/sortable.d.ts.map +1 -1
  114. package/dist/components/sortable/sortable.js +4 -2
  115. package/dist/tsconfig.tsbuildinfo +1 -1
  116. package/llms.txt +1 -1
  117. package/package.json +7 -4
  118. package/src/styles/globals.css +26 -0
@@ -1,11 +1,15 @@
1
1
  import type { ReactNode } from 'react';
2
- import type { DateSelectorValue } from '../date-selector';
2
+ import type { DateSelectorProps, DateSelectorValue } from '../date-selector';
3
+ /** Props forwarded to {@link DateSelector} for date-metric columns. `value` / `onChange` are controlled by Columns. */
4
+ export type ColumnsDateMetricOptions = Omit<DateSelectorProps, 'value' | 'onChange'>;
3
5
  export interface ColumnDefinition {
4
6
  id: string;
5
7
  name: string;
6
8
  description?: string;
7
9
  /** When true, selecting the row opens the date configuration pane. */
8
10
  supportsDateRange?: boolean;
11
+ /** Merged into DateSelector for this column; overrides `Columns` dateMetricOptions. */
12
+ dateMetricOptions?: Partial<ColumnsDateMetricOptions>;
9
13
  icon?: ReactNode;
10
14
  }
11
15
  export interface ColumnFieldGroup {
@@ -16,6 +20,11 @@ export type ColumnsFieldsConfig = (ColumnDefinition | ColumnFieldGroup)[];
16
20
  export interface SelectedColumn {
17
21
  id: string;
18
22
  dateRange?: DateSelectorValue;
23
+ /**
24
+ * Pin state when pin controls are shown (`false` unpinned, `true` pinned).
25
+ * When omitted, pin toggle is hidden for that row.
26
+ */
27
+ pinned?: boolean;
19
28
  }
20
29
  export interface ColumnsI18n {
21
30
  triggerLabel: string;
@@ -27,6 +36,14 @@ export interface ColumnsI18n {
27
36
  columnsSelectedSuffix: (n: number) => string;
28
37
  configureDateRangeHint: string;
29
38
  done: string;
39
+ /** Accessible label for the pin control when the column is unpinned. */
40
+ pinColumn: string;
41
+ /** Accessible label for the pin control when the column is pinned. */
42
+ unpinColumn: string;
43
+ /** Optional heading above the pinned column section. */
44
+ pinnedHeading: string;
45
+ /** Optional heading above the unpinned column section. */
46
+ unpinnedHeading: string;
30
47
  }
31
48
  export declare const DEFAULT_COLUMNS_I18N: ColumnsI18n;
32
49
  export interface ColumnsProps {
@@ -36,5 +53,12 @@ export interface ColumnsProps {
36
53
  className?: string;
37
54
  popoverContentClassName?: string;
38
55
  i18n?: Partial<ColumnsI18n>;
56
+ /** Default DateSelector options for every `supportsDateRange` column; per-column `dateMetricOptions` override. */
57
+ dateMetricOptions?: Partial<ColumnsDateMetricOptions>;
58
+ /**
59
+ * When true: divider between pinned and unpinned sections (when both exist) and reordering stays within each partition.
60
+ * When false: full-list reorder; order is normalized to pinned-first after each move. Pin state is per row via `SelectedColumn.pinned`.
61
+ */
62
+ lockPinning?: boolean;
39
63
  }
40
64
  //# sourceMappingURL=columns-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"columns-types.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,gBAAgB,GAAG,gBAAgB,CAAC,EAAE,CAAC;AAE1E,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,iBAAiB,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,qBAAqB,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,sBAAsB,EAAE,MAAM,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,WASlC,CAAC;AAEF,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC/B"}
1
+ {"version":3,"file":"columns-types.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,uHAAuH;AACvH,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,UAAU,CAAC,CAAC;AAErF,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACtD,IAAI,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,gBAAgB,GAAG,gBAAgB,CAAC,EAAE,CAAC;AAE1E,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,qBAAqB,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,sBAAsB,EAAE,MAAM,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,oBAAoB,EAAE,WAalC,CAAC;AAEF,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5B,kHAAkH;IAClH,iBAAiB,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACtD;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB"}
@@ -7,4 +7,8 @@ export const DEFAULT_COLUMNS_I18N = {
7
7
  columnsSelectedSuffix: (n) => (n === 1 ? 'column selected' : 'columns selected'),
8
8
  configureDateRangeHint: 'Configure date range for this column',
9
9
  done: 'Done',
10
+ pinColumn: 'Pin column',
11
+ unpinColumn: 'Unpin column',
12
+ pinnedHeading: 'Pinned',
13
+ unpinnedHeading: 'Other columns',
10
14
  };
@@ -1,5 +1,35 @@
1
1
  import type { DateSelectorValue } from '../date-selector';
2
- import type { ColumnDefinition, ColumnsFieldsConfig } from './columns-types';
2
+ import type { ColumnDefinition, ColumnsFieldsConfig, SelectedColumn } from './columns-types';
3
+ /** Result of splitting `value` into pinned (prefix) and unpinned (suffix) partitions. */
4
+ export type SplitPinnedPrefixResult = {
5
+ pinned: SelectedColumn[];
6
+ unpinned: SelectedColumn[];
7
+ };
8
+ /**
9
+ * Splits selected columns into pinned vs unpinned rows in display order.
10
+ * Uses `pinned === true` for the pinned partition; all other rows go to unpinned.
11
+ * This fixes malformed arrays where pinned rows are not contiguous at the front.
12
+ */
13
+ export declare function splitPinnedPrefix(value: SelectedColumn[]): SplitPinnedPrefixResult;
14
+ /** Contiguous `pinned` rows from the start of `value` (display boundary for locked drag + section split). */
15
+ export declare function pinnedPrefixLength(value: SelectedColumn[]): number;
16
+ /** Concatenates partitions in pinned-first order. */
17
+ export declare function mergePinnedPartitions(pinned: SelectedColumn[], unpinned: SelectedColumn[]): SelectedColumn[];
18
+ /**
19
+ * Reorders to pinned-first and sets explicit `pinned` flags on every row.
20
+ */
21
+ export declare function normalizePinnedColumnOrder(value: SelectedColumn[]): SelectedColumn[];
22
+ /**
23
+ * Toggles `pinned` for the column with `id`, moving it to the end of the pinned block
24
+ * when pinning or to the start of the unpinned block when unpinning.
25
+ * Unknown ids return a shallow copy of `value` unchanged.
26
+ */
27
+ export declare function toggleColumnPinned(value: SelectedColumn[], id: string): SelectedColumn[];
28
+ /**
29
+ * Applies a reorder from sortable indices; returns `null` when the move is invalid or a no-op.
30
+ * With `lockPinning`, moves across the contiguous pinned / unpinned boundary are rejected.
31
+ */
32
+ export declare function reorderColumnsSelection(value: SelectedColumn[], lockPinning: boolean, activeIndex: number, overIndex: number): SelectedColumn[] | null;
3
33
  /** Secondary label for sortable rows: rolling presets and custom period summaries (e.g. `2024 Q2`). */
4
34
  export declare function formatColumnDateRangeSecondary(value: DateSelectorValue): string | null;
5
35
  export declare function flattenColumns(fields: ColumnsFieldsConfig): ColumnDefinition[];
@@ -1 +1 @@
1
- {"version":3,"file":"columns-utils.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE/F,uGAAuG;AACvG,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAoDtF;AAMD,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,gBAAgB,EAAE,CAO9E;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAC;AAErF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,kBAAkB,EAAE,CAWrF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAE5E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAS5F"}
1
+ {"version":3,"file":"columns-utils.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns-utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE/G,yFAAyF;AACzF,MAAM,MAAM,uBAAuB,GAAG;IAClC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC9B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,uBAAuB,CAWlF;AAED,6GAA6G;AAC7G,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,CAOlE;AAED,qDAAqD;AACrD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAE5G;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAGpF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,EAAE,CAoBxF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,IAAI,CAatJ;AAED,uGAAuG;AACvG,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAoDtF;AAMD,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,gBAAgB,EAAE,CAO9E;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAC;AAErF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,kBAAkB,EAAE,CAWrF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAE5E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAS5F"}
@@ -1,5 +1,90 @@
1
+ import { arrayMove } from '@dnd-kit/sortable';
1
2
  import { format } from 'date-fns';
2
- import { DEFAULT_DATE_SELECTOR_I18N, getRollingSummaryLabel } from '../date-selector';
3
+ import { DEFAULT_DATE_SELECTOR_I18N } from '../date-selector/date-selector-types';
4
+ import { getRollingSummaryLabel } from '../date-selector/date-selector-value';
5
+ /**
6
+ * Splits selected columns into pinned vs unpinned rows in display order.
7
+ * Uses `pinned === true` for the pinned partition; all other rows go to unpinned.
8
+ * This fixes malformed arrays where pinned rows are not contiguous at the front.
9
+ */
10
+ export function splitPinnedPrefix(value) {
11
+ const pinned = [];
12
+ const unpinned = [];
13
+ for (const col of value) {
14
+ if (col.pinned === true) {
15
+ pinned.push(col);
16
+ }
17
+ else {
18
+ unpinned.push(col);
19
+ }
20
+ }
21
+ return { pinned, unpinned };
22
+ }
23
+ /** Contiguous `pinned` rows from the start of `value` (display boundary for locked drag + section split). */
24
+ export function pinnedPrefixLength(value) {
25
+ let n = 0;
26
+ for (const row of value) {
27
+ if (row.pinned === true)
28
+ n++;
29
+ else
30
+ break;
31
+ }
32
+ return n;
33
+ }
34
+ /** Concatenates partitions in pinned-first order. */
35
+ export function mergePinnedPartitions(pinned, unpinned) {
36
+ return [...pinned, ...unpinned];
37
+ }
38
+ /**
39
+ * Reorders to pinned-first and sets explicit `pinned` flags on every row.
40
+ */
41
+ export function normalizePinnedColumnOrder(value) {
42
+ const { pinned, unpinned } = splitPinnedPrefix(value);
43
+ return [...pinned.map((c) => ({ ...c, pinned: true })), ...unpinned.map((c) => ({ ...c, pinned: false }))];
44
+ }
45
+ /**
46
+ * Toggles `pinned` for the column with `id`, moving it to the end of the pinned block
47
+ * when pinning or to the start of the unpinned block when unpinning.
48
+ * Unknown ids return a shallow copy of `value` unchanged.
49
+ */
50
+ export function toggleColumnPinned(value, id) {
51
+ const { pinned: p0, unpinned: u0 } = splitPinnedPrefix(value);
52
+ const pinned = [...p0];
53
+ const unpinned = [...u0];
54
+ const pi = pinned.findIndex((c) => c.id === id);
55
+ if (pi >= 0) {
56
+ const [row] = pinned.splice(pi, 1);
57
+ unpinned.unshift({ ...row, pinned: false });
58
+ return mergePinnedPartitions(pinned, unpinned);
59
+ }
60
+ const ui = unpinned.findIndex((c) => c.id === id);
61
+ if (ui >= 0) {
62
+ const [row] = unpinned.splice(ui, 1);
63
+ pinned.push({ ...row, pinned: true });
64
+ return mergePinnedPartitions(pinned, unpinned);
65
+ }
66
+ return value;
67
+ }
68
+ /**
69
+ * Applies a reorder from sortable indices; returns `null` when the move is invalid or a no-op.
70
+ * With `lockPinning`, moves across the contiguous pinned / unpinned boundary are rejected.
71
+ */
72
+ export function reorderColumnsSelection(value, lockPinning, activeIndex, overIndex) {
73
+ if (activeIndex < 0 || overIndex < 0 || activeIndex === overIndex)
74
+ return null;
75
+ const pc = pinnedPrefixLength(value);
76
+ if (lockPinning) {
77
+ const activeInPinned = activeIndex < pc;
78
+ const overInPinned = overIndex < pc;
79
+ if (activeInPinned !== overInPinned)
80
+ return null;
81
+ }
82
+ let next = arrayMove(value, activeIndex, overIndex);
83
+ if (!lockPinning) {
84
+ next = normalizePinnedColumnOrder(next);
85
+ }
86
+ return next;
87
+ }
3
88
  /** Secondary label for sortable rows: rolling presets and custom period summaries (e.g. `2024 Q2`). */
4
89
  export function formatColumnDateRangeSecondary(value) {
5
90
  const i18n = DEFAULT_DATE_SELECTOR_I18N;
@@ -1,3 +1,3 @@
1
1
  import type { ColumnsProps } from './columns-types';
2
- export declare function Columns({ columns, value, onChange, className, popoverContentClassName, i18n: i18nPartial }: ColumnsProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function Columns({ columns, value, onChange, className, popoverContentClassName, i18n: i18nPartial, dateMetricOptions: dateMetricOptionsProp, lockPinning, }: ColumnsProps): import("react/jsx-runtime").JSX.Element;
3
3
  //# sourceMappingURL=columns.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,iBAAiB,CAAC;AAIpE,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,uBAAuB,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,YAAY,2CA2RxH"}
1
+ {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAA4B,YAAY,EAAkB,MAAM,iBAAiB,CAAC;AAsB9F,wBAAgB,OAAO,CAAC,EACpB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,SAAS,EACT,uBAAuB,EACvB,IAAI,EAAE,WAAW,EACjB,iBAAiB,EAAE,qBAAqB,EACxC,WAAmB,GACtB,EAAE,YAAY,2CA+Wd"}
@@ -1,7 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useMemo, useState } from 'react';
4
- import { ChevronDown, ChevronRight, Columns3, GripVertical, X } from 'lucide-react';
4
+ import { closestCenter } from '@dnd-kit/core';
5
+ import { ChevronDown, ChevronRight, Columns3, GripVertical, Pin, PinOff, X } from 'lucide-react';
5
6
  import { Button } from '../../components/button';
6
7
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../../components/collapsible';
7
8
  import { Popover, PopoverContent, PopoverTrigger } from '../../components/popover';
@@ -11,8 +12,18 @@ import { Sortable, SortableItem, SortableItemHandle } from '../../components/sor
11
12
  import { cn } from '../../lib/utils';
12
13
  import { DateSelector } from '../date-selector';
13
14
  import { DEFAULT_COLUMNS_I18N } from './columns-types';
14
- import { buildColumnMap, formatColumnDateRangeSecondary, parseColumnSections } from './columns-utils';
15
- export function Columns({ columns, value, onChange, className, popoverContentClassName, i18n: i18nPartial }) {
15
+ import { buildColumnMap, formatColumnDateRangeSecondary, parseColumnSections, pinnedPrefixLength, reorderColumnsSelection, toggleColumnPinned, } from './columns-utils';
16
+ const DEFAULT_DATE_METRIC_OPTIONS = {
17
+ showRollingPresets: true,
18
+ periodTypes: ['day', 'month', 'quarter', 'year'],
19
+ separateDayPeriod: true,
20
+ showInput: false,
21
+ showOperators: false,
22
+ defaultPeriodType: 'day',
23
+ defaultFilterType: 'between',
24
+ fillHeight: true,
25
+ };
26
+ export function Columns({ columns, value, onChange, className, popoverContentClassName, i18n: i18nPartial, dateMetricOptions: dateMetricOptionsProp, lockPinning = false, }) {
16
27
  const i18n = useMemo(() => ({ ...DEFAULT_COLUMNS_I18N, ...i18nPartial }), [i18nPartial]);
17
28
  const columnMap = useMemo(() => buildColumnMap(columns), [columns]);
18
29
  const sections = useMemo(() => parseColumnSections(columns), [columns]);
@@ -21,6 +32,15 @@ export function Columns({ columns, value, onChange, className, popoverContentCla
21
32
  const selectedIdSet = useMemo(() => new Set(value.map((v) => v.id)), [value]);
22
33
  const activeDef = activeColumnId ? columnMap[activeColumnId] : undefined;
23
34
  const showRightPanel = Boolean(activeDef?.supportsDateRange && activeColumnId && selectedIdSet.has(activeColumnId));
35
+ const mergedDateMetricOptions = useMemo(() => {
36
+ const colOpts = activeDef?.supportsDateRange ? activeDef.dateMetricOptions : undefined;
37
+ return {
38
+ ...DEFAULT_DATE_METRIC_OPTIONS,
39
+ ...dateMetricOptionsProp,
40
+ ...colOpts,
41
+ };
42
+ }, [activeDef, dateMetricOptionsProp]);
43
+ const { className: dateMetricClassName, ...dateSelectorProps } = mergedDateMetricOptions;
24
44
  const activeSelected = activeColumnId ? value.find((s) => s.id === activeColumnId) : undefined;
25
45
  const activeDateRange = activeSelected?.dateRange;
26
46
  const handleOpenChange = useCallback((next) => {
@@ -48,23 +68,64 @@ export function Columns({ columns, value, onChange, className, popoverContentCla
48
68
  const handleRowActivate = useCallback((id) => {
49
69
  setActiveColumnId(id);
50
70
  }, []);
71
+ const pinnedPrefixLen = useMemo(() => pinnedPrefixLength(value), [value]);
72
+ const unpinnedCount = value.length - pinnedPrefixLen;
73
+ const lockedPartitionCollision = useMemo(() => {
74
+ if (!lockPinning)
75
+ return undefined;
76
+ return (args) => {
77
+ const collisions = closestCenter(args);
78
+ const pc = pinnedPrefixLength(value);
79
+ const activeIdx = value.findIndex((item) => item.id === String(args.active.id));
80
+ if (activeIdx < 0 || !collisions?.length)
81
+ return collisions;
82
+ const activeInPinned = activeIdx < pc;
83
+ const filtered = collisions.filter((c) => {
84
+ const overIdx = value.findIndex((item) => item.id === String(c.id));
85
+ if (overIdx < 0)
86
+ return false;
87
+ return overIdx < pc === activeInPinned;
88
+ });
89
+ return filtered;
90
+ };
91
+ }, [lockPinning, value]);
92
+ const handleTogglePin = useCallback((id) => {
93
+ onChange(toggleColumnPinned(value, id));
94
+ }, [onChange, value]);
95
+ const handleColumnsMove = useCallback(({ activeIndex, overIndex }) => {
96
+ const next = reorderColumnsSelection(value, lockPinning, activeIndex, overIndex);
97
+ if (next)
98
+ onChange(next);
99
+ }, [value, onChange, lockPinning]);
51
100
  const count = value.length;
52
- return (_jsx("div", { className: cn(className), children: _jsxs(Popover, { open: open, onOpenChange: handleOpenChange, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "gap-2 font-normal", "aria-expanded": open, "aria-haspopup": "dialog" }), children: [_jsx(Columns3, { className: "size-4 text-primary", "aria-hidden": true }), _jsxs("span", { className: "text-sm", children: [i18n.triggerLabel, " \u00B7 ", i18n.triggerCount(count)] }), _jsx(ChevronDown, { className: "size-4 opacity-60", "aria-hidden": true })] }), _jsx(PopoverContent, { align: "start", className: cn('w-auto max-w-[min(100vw-2rem,720px)] gap-0 overflow-hidden p-0 shadow-lg ring-1 ring-foreground/10', showRightPanel ? 'min-w-[min(100vw-2rem,720px)]' : 'min-w-[min(100vw-2rem,340px)]', popoverContentClassName), children: _jsxs("div", { className: "flex max-h-[min(85vh,560px)] min-h-0", children: [_jsxs("div", { className: cn('flex min-h-0 min-w-0 flex-1 flex-col border-e border-border', showRightPanel ? 'max-w-[340px]' : 'w-full max-w-[360px]'), children: [_jsx("div", { className: "shrink-0 space-y-1 border-b border-border px-3 py-2.5", children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium", children: i18n.addColumnsTitle }), _jsx("p", { className: "text-xs text-muted-foreground", children: i18n.addColumnsSubtitle })] }), _jsx("button", { type: "button", onClick: clearAll, disabled: count === 0, className: "shrink-0 text-xs font-medium text-primary hover:underline disabled:pointer-events-none disabled:opacity-40", children: i18n.clearAll })] }) }), _jsx(ScrollArea, { className: "min-h-0 flex-1", children: _jsxs("div", { className: "flex flex-col gap-2 px-3 py-2", children: [value.length > 0 ? (_jsx(Sortable, { value: value, onValueChange: onChange, getItemValue: (item) => item.id, className: "flex flex-col gap-2", children: value.map((row) => {
53
- const def = columnMap[row.id];
54
- if (!def)
55
- return null;
56
- const isActive = activeColumnId === row.id;
57
- const secondary = row.dateRange && def.supportsDateRange ? formatColumnDateRangeSecondary(row.dateRange) : null;
58
- const showHighlight = isActive && def.supportsDateRange;
59
- return (_jsxs(SortableItem, { value: row.id, className: cn('flex min-w-0 items-center gap-2 rounded-md border px-2 py-1.5 text-sm shadow-xs transition-colors', showHighlight
60
- ? 'border-primary bg-primary text-primary-foreground'
61
- : isActive
62
- ? 'border-primary/40 bg-primary/5'
63
- : 'border-border bg-card text-card-foreground'), children: [_jsx(SortableItemHandle, { className: cn('shrink-0 touch-none', showHighlight ? 'text-primary-foreground/80' : 'text-muted-foreground'), "aria-label": `Reorder ${def.name}`, children: _jsx(GripVertical, { className: "size-4", strokeWidth: 2.5 }) }), _jsxs("button", { type: "button", className: "flex min-w-0 flex-1 items-center gap-1.5 text-start", onClick: () => handleRowActivate(row.id), children: [_jsx("span", { className: cn('truncate font-medium', !secondary && 'flex-1'), children: def.name }), secondary ? (_jsxs("span", { className: cn('truncate text-xs', showHighlight ? 'text-primary-foreground/85' : 'text-muted-foreground'), children: ["\u00B7 ", secondary] })) : null] }), _jsx("button", { type: "button", className: cn('shrink-0 rounded-sm p-0.5 outline-none hover:bg-black/10 focus-visible:ring-2 focus-visible:ring-ring', showHighlight && 'hover:bg-white/15'), "aria-label": `Remove ${def.name}`, onClick: (e) => {
64
- e.stopPropagation();
65
- removeColumn(row.id);
66
- }, children: _jsx(X, { className: "size-4" }) })] }, row.id));
67
- }) })) : (_jsx("p", { className: "text-xs text-muted-foreground", children: "No columns selected." })), _jsx(Separator, { className: "my-1" }), _jsx("div", { className: "flex flex-col gap-2 pb-2", children: sections.map((section, si) => {
101
+ return (_jsx("div", { className: cn(className), children: _jsxs(Popover, { open: open, onOpenChange: handleOpenChange, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "gap-2 font-normal", "aria-expanded": open, "aria-haspopup": "dialog" }), children: [_jsx(Columns3, { className: "size-4 text-primary", "aria-hidden": true }), _jsxs("span", { className: "text-sm", children: [i18n.triggerLabel, " \u00B7 ", i18n.triggerCount(count)] }), _jsx(ChevronDown, { className: "size-4 opacity-60", "aria-hidden": true })] }), _jsx(PopoverContent, { align: "start", className: cn('w-auto max-w-[min(100vw-2rem,720px)] gap-0 overflow-hidden p-0 shadow-lg ring-1 ring-foreground/10', showRightPanel ? 'min-w-[min(100vw-2rem,720px)]' : 'min-w-[min(100vw-2rem,340px)]', popoverContentClassName), children: _jsxs("div", { className: "flex max-h-[min(85vh,560px)] min-h-0", children: [_jsxs("div", { className: cn('flex min-h-0 min-w-0 flex-1 flex-col border-e border-border', showRightPanel ? 'max-w-[340px]' : 'w-full max-w-[360px]'), children: [_jsx("div", { className: "shrink-0 space-y-1 border-b border-border px-3 py-2.5", children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium", children: i18n.addColumnsTitle }), _jsx("p", { className: "text-xs text-muted-foreground", children: i18n.addColumnsSubtitle })] }), _jsx("button", { type: "button", onClick: clearAll, disabled: count === 0, className: "shrink-0 text-xs font-medium text-primary hover:underline disabled:pointer-events-none disabled:opacity-40", children: i18n.clearAll })] }) }), _jsx(ScrollArea, { className: "min-h-0 flex-1", children: _jsxs("div", { className: "flex flex-col gap-2 px-3 py-2", children: [value.length > 0 ? (_jsxs("div", { className: "flex flex-col gap-2", children: [lockPinning && pinnedPrefixLen > 0 ? (_jsx("p", { className: "px-0.5 text-xs font-medium text-muted-foreground", children: i18n.pinnedHeading })) : null, _jsx(Sortable, { value: value, onValueChange: onChange, getItemValue: (item) => item.id, onMove: handleColumnsMove, collisionDetection: lockedPartitionCollision, className: "flex flex-col gap-2", children: value.flatMap((row, index) => {
102
+ const def = columnMap[row.id];
103
+ if (!def)
104
+ return [];
105
+ const isActive = activeColumnId === row.id;
106
+ const secondary = row.dateRange && def.supportsDateRange ? formatColumnDateRangeSecondary(row.dateRange) : null;
107
+ const showHighlight = isActive && def.supportsDateRange;
108
+ const isPinned = row.pinned === true;
109
+ const showSectionDivider = lockPinning && pinnedPrefixLen > 0 && unpinnedCount > 0 && index === pinnedPrefixLen;
110
+ const item = (_jsxs(SortableItem, { value: row.id, className: cn('flex min-w-0 items-center gap-2 rounded-md border px-2 py-1.5 text-sm shadow-xs transition-colors', showHighlight
111
+ ? 'border-primary bg-primary text-primary-foreground'
112
+ : isActive
113
+ ? 'border-primary/40 bg-primary/5'
114
+ : 'border-border bg-card text-card-foreground'), children: [_jsx(SortableItemHandle, { className: cn('shrink-0 touch-none', showHighlight ? 'text-primary-foreground/80' : 'text-muted-foreground'), "aria-label": `Reorder ${def.name}`, children: _jsx(GripVertical, { className: "size-4", strokeWidth: 2.5 }) }), !lockPinning && row.pinned !== undefined ? (_jsx("button", { type: "button", className: cn('shrink-0 rounded-sm p-0.5 outline-none hover:bg-black/10 focus-visible:ring-2 focus-visible:ring-ring', showHighlight && 'hover:bg-white/15'), "aria-pressed": isPinned, "aria-label": isPinned ? i18n.unpinColumn : i18n.pinColumn, onClick: (e) => {
115
+ e.stopPropagation();
116
+ handleTogglePin(row.id);
117
+ }, children: isPinned ? (_jsx(PinOff, { className: "size-4", "aria-hidden": true })) : (_jsx(Pin, { className: "size-4 opacity-70", "aria-hidden": true })) })) : null, _jsxs("button", { type: "button", className: "flex min-w-0 flex-1 items-center gap-1.5 text-start", onClick: () => handleRowActivate(row.id), children: [_jsx("span", { className: cn('truncate font-medium', !secondary && 'flex-1'), children: def.name }), secondary ? (_jsxs("span", { className: cn('truncate text-xs', showHighlight ? 'text-primary-foreground/85' : 'text-muted-foreground'), children: ["\u00B7 ", secondary] })) : null] }), _jsx("button", { type: "button", className: cn('shrink-0 rounded-sm p-0.5 outline-none hover:bg-black/10 focus-visible:ring-2 focus-visible:ring-ring', showHighlight && 'hover:bg-white/15'), "aria-label": `Remove ${def.name}`, onClick: (e) => {
118
+ e.stopPropagation();
119
+ removeColumn(row.id);
120
+ }, children: _jsx(X, { className: "size-4" }) })] }, row.id));
121
+ if (showSectionDivider) {
122
+ return [
123
+ _jsxs("div", { className: "flex flex-col gap-2 px-0.5 pt-0.5", children: [_jsx(Separator, { className: "bg-border" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: i18n.unpinnedHeading })] }, `columns-unpinned-heading-${row.id}`),
124
+ item,
125
+ ];
126
+ }
127
+ return [item];
128
+ }) })] })) : (_jsx("p", { className: "text-xs text-muted-foreground", children: "No columns selected." })), _jsx(Separator, { className: "my-1" }), _jsx("div", { className: "flex flex-col gap-2 pb-2", children: sections.map((section, si) => {
68
129
  const available = section.fields.filter((f) => !selectedIdSet.has(f.id));
69
130
  if (available.length === 0)
70
131
  return null;
@@ -75,5 +136,5 @@ export function Columns({ columns, value, onChange, className, popoverContentCla
75
136
  }
76
137
  const heading = section.groupLabel ?? (available.length > 1 ? 'Columns' : (available[0]?.name ?? 'Columns'));
77
138
  return (_jsxs(Collapsible, { defaultOpen: true, className: "flex flex-col gap-1", children: [_jsxs(CollapsibleTrigger, { className: "group flex w-full items-center gap-2 rounded-md px-1 py-1.5 text-start text-sm font-medium hover:bg-muted/80", type: "button", children: [available[0]?.icon, _jsx("span", { className: "min-w-0 flex-1 truncate", children: heading }), _jsx(ChevronRight, { className: "size-4 shrink-0 text-muted-foreground transition-transform group-data-[panel-open]:rotate-90" })] }), _jsx(CollapsibleContent, { className: "flex flex-col gap-0.5 ps-1", children: available.map((field) => (_jsx("button", { type: "button", className: "rounded-md px-2 py-1.5 text-start text-sm text-foreground hover:bg-accent hover:text-accent-foreground", onClick: () => addColumn(field.id), children: field.name }, field.id))) })] }, si));
78
- }) })] }) }), _jsxs("div", { className: "shrink-0 border-t border-border px-3 py-2 text-xs text-muted-foreground", children: [_jsx("span", { className: "font-medium text-primary tabular-nums", children: count }), " ", i18n.columnsSelectedSuffix(count)] })] }), showRightPanel && activeColumnId && activeDef?.supportsDateRange ? (_jsxs("div", { className: "flex min-h-0 w-[min(100%,380px)] min-w-[280px] flex-1 flex-col border-border bg-popover", children: [_jsxs("div", { className: "flex shrink-0 items-start justify-between gap-2 border-b border-border px-3 py-2.5", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium", children: activeDef.name }), _jsx("p", { className: "text-xs text-muted-foreground", children: activeDef.description ?? i18n.configureDateRangeHint })] }), _jsx("button", { type: "button", className: "shrink-0 rounded-sm p-1 text-muted-foreground hover:bg-muted hover:text-foreground", "aria-label": "Close configuration", onClick: () => setActiveColumnId(null), children: _jsx(X, { className: "size-4" }) })] }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: _jsx("div", { className: "flex min-h-0 flex-1 flex-col p-3", children: _jsx(DateSelector, { value: activeDateRange, onChange: (next) => updateSelectedDateRange(activeColumnId, next), showRollingPresets: true, periodTypes: ['day', 'month', 'quarter', 'year'], separateDayPeriod: true, showInput: false, showOperators: false, defaultPeriodType: "day", defaultFilterType: "between", fillHeight: true, className: "min-h-0 max-w-none flex-1" }) }) }), _jsx("div", { className: "flex shrink-0 items-center justify-end gap-2 border-t border-border px-3 py-2.5", children: _jsx(Button, { type: "button", size: "sm", onClick: () => setActiveColumnId(null), children: i18n.done }) })] })) : null] }) })] }) }));
139
+ }) })] }) }), _jsxs("div", { className: "shrink-0 border-t border-border px-3 py-2 text-xs text-muted-foreground", children: [_jsx("span", { className: "font-medium text-primary tabular-nums", children: count }), " ", i18n.columnsSelectedSuffix(count)] })] }), showRightPanel && activeColumnId && activeDef?.supportsDateRange ? (_jsxs("div", { className: "flex min-h-0 w-[min(100%,380px)] min-w-[280px] flex-1 flex-col border-border bg-popover", children: [_jsxs("div", { className: "flex shrink-0 items-start justify-between gap-2 border-b border-border px-3 py-2.5", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium", children: activeDef.name }), _jsx("p", { className: "text-xs text-muted-foreground", children: activeDef.description ?? i18n.configureDateRangeHint })] }), _jsx("button", { type: "button", className: "shrink-0 rounded-sm p-1 text-muted-foreground hover:bg-muted hover:text-foreground", "aria-label": "Close configuration", onClick: () => setActiveColumnId(null), children: _jsx(X, { className: "size-4" }) })] }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: _jsx("div", { className: "flex min-h-0 flex-1 flex-col p-3", children: _jsx(DateSelector, { ...dateSelectorProps, value: activeDateRange, onChange: (next) => updateSelectedDateRange(activeColumnId, next), className: cn('min-h-0 max-w-none flex-1', dateMetricClassName) }) }) }), _jsx("div", { className: "flex shrink-0 items-center justify-end gap-2 border-t border-border px-3 py-2.5", children: _jsx(Button, { type: "button", size: "sm", onClick: () => setActiveColumnId(null), children: i18n.done }) })] })) : null] }) })] }) }));
79
140
  }
@@ -9,4 +9,5 @@ declare const meta: {
9
9
  export default meta;
10
10
  type Story = StoryObj<typeof meta>;
11
11
  export declare const Default: Story;
12
+ export declare const PinningLocked: Story;
12
13
  //# sourceMappingURL=columns.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"columns.stories.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAqEvD,QAAA,MAAM,IAAI;;;;;;CAMM,CAAC;AAEjB,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC"}
1
+ {"version":3,"file":"columns.stories.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/columns.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AA6FvD,QAAA,MAAM,IAAI;;;;;;CAMM,CAAC;AAEjB,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC"}
@@ -49,11 +49,28 @@ const catalog = [
49
49
  ].map((f) => ({ ...f, icon: _jsx(BarChart3, { className: "size-4 text-muted-foreground", "aria-hidden": true }) })),
50
50
  },
51
51
  ];
52
- const initialSelected = [{ id: 'bedrooms' }, { id: 'bathrooms' }, { id: 'occupancy', dateRange: rolling180 }, { id: 'adr' }];
52
+ const initialSelected = [
53
+ { id: 'bedrooms', pinned: false },
54
+ { id: 'bathrooms', pinned: false },
55
+ { id: 'occupancy', dateRange: rolling180, pinned: false },
56
+ { id: 'adr', pinned: false },
57
+ ];
58
+ /** Pinned-first order: pinned prefix, then unpinned. */
59
+ const initialPinningLocked = [
60
+ { id: 'bedrooms', pinned: true },
61
+ { id: 'address', pinned: true },
62
+ { id: 'bathrooms' },
63
+ { id: 'occupancy', dateRange: rolling180 },
64
+ { id: 'adr' },
65
+ ];
53
66
  function ColumnsPlayground() {
54
67
  const [value, setValue] = useState(initialSelected);
55
68
  return (_jsx("div", { className: "p-6", children: _jsx(Columns, { columns: catalog, value: value, onChange: setValue }) }));
56
69
  }
70
+ function ColumnsPinningLockedPlayground() {
71
+ const [value, setValue] = useState(initialPinningLocked);
72
+ return (_jsx("div", { className: "p-6", children: _jsx(Columns, { columns: catalog, value: value, onChange: setValue, lockPinning: true }) }));
73
+ }
57
74
  const meta = {
58
75
  title: 'Blocks/Columns',
59
76
  tags: ['autodocs'],
@@ -65,3 +82,6 @@ export default meta;
65
82
  export const Default = {
66
83
  render: () => _jsx(ColumnsPlayground, {}),
67
84
  };
85
+ export const PinningLocked = {
86
+ render: () => _jsx(ColumnsPinningLockedPlayground, {}),
87
+ };
@@ -1,5 +1,5 @@
1
1
  export { Columns } from './columns';
2
- export type { ColumnDefinition, ColumnFieldGroup, ColumnsFieldsConfig, ColumnsI18n, ColumnsProps, SelectedColumn } from './columns-types';
2
+ export type { ColumnDefinition, ColumnFieldGroup, ColumnsDateMetricOptions, ColumnsFieldsConfig, ColumnsI18n, ColumnsProps, SelectedColumn, } from './columns-types';
3
3
  export { DEFAULT_COLUMNS_I18N } from './columns-types';
4
4
  export { buildColumnMap, flattenColumns, formatColumnDateRangeSecondary, hasTopLevelColumnGroups, parseColumnSections } from './columns-utils';
5
5
  export type { ColumnsMenuSection } from './columns-utils';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC1I,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC/I,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/columns/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EACR,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,EACnB,WAAW,EACX,YAAY,EACZ,cAAc,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC/I,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { type PopoverTriggerProps } from '../../components/popover';
3
+ export type FloatingMenuWidgetRenderTrigger = NonNullable<PopoverTriggerProps['render']>;
4
+ export type FloatingMenuWidgetInset = {
5
+ right: number;
6
+ bottom: number;
7
+ };
8
+ export type FloatingMenuWidgetProps = Omit<React.ComponentProps<'div'>, 'children'> & {
9
+ children: React.ReactNode;
10
+ menuTitle: string;
11
+ menuDescription?: string;
12
+ triggerIcon?: React.ReactNode;
13
+ renderTrigger?: FloatingMenuWidgetRenderTrigger;
14
+ /** Set `false` when `renderTrigger` is not a native `<button>`. */
15
+ triggerNativeButton?: boolean;
16
+ defaultInset?: Partial<FloatingMenuWidgetInset>;
17
+ /** Unique key for persisting position in `localStorage`. */
18
+ positionStorageKey?: string;
19
+ /** Long-press to drag. */
20
+ draggable?: boolean;
21
+ open?: boolean;
22
+ onOpenChange?: (open: boolean) => void;
23
+ };
24
+ declare function FloatingMenuWidget({ children, menuTitle, menuDescription, triggerIcon, renderTrigger, triggerNativeButton, defaultInset, positionStorageKey, draggable, open: openControlled, onOpenChange: onOpenChangeControlled, className, ...rest }: FloatingMenuWidgetProps): import("react/jsx-runtime").JSX.Element;
25
+ export { FloatingMenuWidget };
26
+ //# sourceMappingURL=floating-menu-widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floating-menu-widget.d.ts","sourceRoot":"","sources":["../../../src/blocks/floating-menu-widget/floating-menu-widget.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,EAA2C,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAS7G,MAAM,MAAM,+BAA+B,GAAG,WAAW,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEzF,MAAM,MAAM,uBAAuB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IAClF,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,aAAa,CAAC,EAAE,+BAA+B,CAAC;IAChD,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAChD,4DAA4D;IAC5D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C,CAAC;AA0BF,iBAAS,kBAAkB,CAAC,EACxB,QAAQ,EACR,SAAS,EACT,eAAe,EACf,WAAyC,EACzC,aAAa,EACb,mBAA0B,EAC1B,YAAY,EACZ,kBAAkB,EAClB,SAAgB,EAChB,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,sBAAsB,EACpC,SAAS,EACT,GAAG,IAAI,EACV,EAAE,uBAAuB,2CAwOzB;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}