@gridkitjs/core 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -23,11 +23,30 @@ type FieldPath<Row> = {
23
23
  type ColumnType = "dateTime" | "date" | "time" | "number" | "string" | "boolean" | "decimal" | "currency" | "percent";
24
24
  type ColumnAlignment = "left" | "center" | "right";
25
25
  /**
26
- * @typeParam Header - What a header renders to. This package is
26
+ * What `cellTemplate` receives. An object rather than positional arguments so
27
+ * that a later addition — a sort state, whether the row is selected — is not a
28
+ * breaking change to every template already written.
29
+ */
30
+ interface CellTemplateContext<Row> {
31
+ /**
32
+ * This cell's value, read off the column's field path. `unknown` because a
33
+ * dotted path's type cannot be recovered here; a template narrows it.
34
+ */
35
+ value: unknown;
36
+ /** The whole row, for a template that needs a sibling field. */
37
+ row: Row;
38
+ /**
39
+ * Position among the rows as rendered. Once paging exists this is the index
40
+ * within the page; an index into the whole dataset would be a second field.
41
+ */
42
+ rowIndex: number;
43
+ }
44
+ /**
45
+ * @typeParam Node - What a header or cell renders to. This package is
27
46
  * framework-agnostic so it defaults to `string`; `@gridkitjs/react` binds it to
28
- * `ReactNode` so a header callback can return JSX.
47
+ * `ReactNode` so a template can return JSX.
29
48
  */
30
- interface ColumnDefinition<Row, Header = string> {
49
+ interface ColumnDefinition<Row, Node = string> {
31
50
  /**
32
51
  * Path to this cell's value: a key of `Row`, or `"Parent.Child"` for a leaf
33
52
  * one level inside a nested object. Any string still compiles — the union
@@ -41,7 +60,13 @@ interface ColumnDefinition<Row, Header = string> {
41
60
  */
42
61
  id?: string;
43
62
  /** Header content, or a function returning it at render time. */
44
- header?: Header | (() => Header) | undefined;
63
+ headerTemplate?: Node | (() => Node) | undefined;
64
+ /**
65
+ * Renders this column's cells in place of the raw value at `field`. The
66
+ * value is still resolved and handed over, so a template that only formats
67
+ * it never repeats the field path.
68
+ */
69
+ cellTemplate?: ((context: CellTemplateContext<Row>) => Node) | undefined;
45
70
  /**
46
71
  * The value type of this column's cells. This package is framework-agnostic so it
47
72
  * defaults to `string`;
@@ -95,8 +120,8 @@ interface ColumnResolveOptions {
95
120
  * decision made once here rather than in each adapter, so a second framework
96
121
  * binding renders identically without repeating the logic.
97
122
  */
98
- interface ResolvedColumn<Row, Header = string> {
99
- column: ColumnDefinition<Row, Header>;
123
+ interface ResolvedColumn<Row, Node = string> {
124
+ column: ColumnDefinition<Row, Node>;
100
125
  id: string;
101
126
  width: number;
102
127
  /**
@@ -106,11 +131,11 @@ interface ResolvedColumn<Row, Header = string> {
106
131
  */
107
132
  sized: boolean;
108
133
  /**
109
- * What the header shows: the column's own `header`, or a label read off the
110
- * field path. `string` is in the union because that fallback is text
111
- * whatever `Header` is bound to.
134
+ * What the header shows: the column's own `headerTemplate`, or a label read
135
+ * off the field path. `string` is in the union because that fallback is text
136
+ * whatever `Node` is bound to.
112
137
  */
113
- label: Header | string;
138
+ label: Node | string;
114
139
  /** Whether this column can be resized, after the grid-level default. */
115
140
  resizable: boolean;
116
141
  /** How this column's cells align, after falling back to its type. */
@@ -159,11 +184,11 @@ declare function defineColumnsFromRows<Row>(rows: readonly Row[]): readonly Colu
159
184
  */
160
185
  declare function getColumnId<Row>(column: ColumnDefinition<Row, unknown>): string;
161
186
  /**
162
- * What a column's header shows: its own `header`, called first if it is a
163
- * function, or else a label read off the field path — `"Application.Id"`
187
+ * What a column's header shows: its own `headerTemplate`, called first if it is
188
+ * a function, or else a label read off the field path — `"Application.Id"`
164
189
  * reading as `"Application Id"`.
165
190
  */
166
- declare function resolveColumnLabel<Row, Header>(column: ColumnDefinition<Row, Header>): Header | string;
191
+ declare function resolveColumnLabel<Row, Node>(column: ColumnDefinition<Row, Node>): Node | string;
167
192
  /**
168
193
  * Accesses a value at a dotted path, e.g. `foo.bar.baz`.
169
194
  *
@@ -197,8 +222,8 @@ declare function clampColumnWidth(width: number, constraints: ColumnConstraints)
197
222
  * The sizing state winning means a user's drag outlives a re-render; a column
198
223
  * absent from it still tracks a `width` edited in the definition.
199
224
  */
200
- declare function resolveColumnWidths<Row, Header>(columns: readonly ColumnDefinition<Row, Header>[], sizing: ColumnSizingState, options?: ColumnResolveOptions): readonly ResolvedColumn<Row, Header>[];
201
- declare function totalColumnWidth<Row, Header>(resolved: readonly ResolvedColumn<Row, Header>[]): number;
225
+ declare function resolveColumnWidths<Row, Node>(columns: readonly ColumnDefinition<Row, Node>[], sizing: ColumnSizingState, options?: ColumnResolveOptions): readonly ResolvedColumn<Row, Node>[];
226
+ declare function totalColumnWidth<Row, Node>(resolved: readonly ResolvedColumn<Row, Node>[]): number;
202
227
  /**
203
228
  * Opens a resize. The session captures its constraints and starting point up
204
229
  * front, so applying a pointer position later is arithmetic on numbers alone —
@@ -220,7 +245,7 @@ declare function applyColumnResize(session: ColumnResizeSession, position: numbe
220
245
  * them. Columns the user has sized are left at that width and the rest share
221
246
  * what is left, so dragging one column does not rubber-band it back.
222
247
  */
223
- declare function fitColumnsToWidth<Row, Header>(resolved: readonly ResolvedColumn<Row, Header>[], availableWidth: number, defaults?: Partial<ColumnSizeDefaults>): readonly ResolvedColumn<Row, Header>[];
248
+ declare function fitColumnsToWidth<Row, Node>(resolved: readonly ResolvedColumn<Row, Node>[], availableWidth: number, defaults?: Partial<ColumnSizeDefaults>): readonly ResolvedColumn<Row, Node>[];
224
249
  /**
225
250
  * The width a column needs to show `measuredWidth` of content.
226
251
  *
@@ -229,4 +254,4 @@ declare function fitColumnsToWidth<Row, Header>(resolved: readonly ResolvedColum
229
254
  */
230
255
  declare function sizeColumnToContent(measuredWidth: number, constraints: ColumnConstraints, padding?: number): number;
231
256
 
232
- export { type ColumnAlignment, type ColumnConstraints, type ColumnDefinition, type ColumnResizeEvent, type ColumnResizeSession, type ColumnResolveOptions, type ColumnSizeDefaults, type ColumnSizingState, type ColumnType, DEFAULT_COLUMN_SIZES, type FieldPath, KEYBOARD_STEP, type ResolvedColumn, accessDotted, alignmentForType, applyColumnResize, beginColumnResize, clampColumnWidth, defineColumnsFromRows, fitColumnsToWidth, getColumnId, resolveColumnConstraints, resolveColumnLabel, resolveColumnWidths, sizeColumnToContent, totalColumnWidth };
257
+ export { type CellTemplateContext, type ColumnAlignment, type ColumnConstraints, type ColumnDefinition, type ColumnResizeEvent, type ColumnResizeSession, type ColumnResolveOptions, type ColumnSizeDefaults, type ColumnSizingState, type ColumnType, DEFAULT_COLUMN_SIZES, type FieldPath, KEYBOARD_STEP, type ResolvedColumn, accessDotted, alignmentForType, applyColumnResize, beginColumnResize, clampColumnWidth, defineColumnsFromRows, fitColumnsToWidth, getColumnId, resolveColumnConstraints, resolveColumnLabel, resolveColumnWidths, sizeColumnToContent, totalColumnWidth };
package/dist/index.js CHANGED
@@ -61,11 +61,11 @@ function getColumnId(column) {
61
61
  return column.id ?? column.field;
62
62
  }
63
63
  function resolveColumnLabel(column) {
64
- const { header } = column;
65
- if (header === void 0) {
64
+ const { headerTemplate } = column;
65
+ if (headerTemplate === void 0) {
66
66
  return column.field.split(".").join(" ");
67
67
  }
68
- return typeof header === "function" ? header() : header;
68
+ return typeof headerTemplate === "function" ? headerTemplate() : headerTemplate;
69
69
  }
70
70
  function accessDotted(obj, path) {
71
71
  return path.split(".").reduce((value, key) => isNested(value) ? value[key] : void 0, obj);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/util/grid.ts","../src/util/sizing.ts"],"sourcesContent":["import type { ColumnAlignment, ColumnDefinition, ColumnType } from \"../types\";\n\n/**\n * Narrows to a value worth drilling into. Mirrors the `LeafValue` union in\n * `../types`: arrays and `Date`s are cell values rather than groups of\n * columns, and `null` cannot be introspected. Keep the two in sync.\n */\nfunction isNested(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\nfunction getType(value: unknown): ColumnType {\n if (value instanceof Date) return \"dateTime\";\n if (typeof value === \"string\") return \"string\";\n if (typeof value === \"number\") return \"number\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"bigint\") return \"number\";\n if (typeof value === \"symbol\") return \"string\";\n return \"string\";\n}\n\n/**\n * How a column of `type` aligns by default. Numeric types align right so that\n * their digits line up by place value; everything else reads from the left.\n */\nexport function alignmentForType(type: ColumnType): ColumnAlignment {\n switch (type) {\n case \"number\":\n case \"decimal\":\n case \"currency\":\n case \"percent\":\n return \"right\";\n default:\n return \"left\";\n }\n}\n\nfunction getAlignment(value: unknown): ColumnAlignment {\n return alignmentForType(getType(value));\n}\n\n/**\n * Derives one column per field across `rows`, in the order each field is first\n * seen — so a key that only appears in a later row still gets a column,\n * appended at the end. Nested objects contribute their non-object properties\n * as `\"Parent.Child\"`; anything deeper is skipped, as is the nested object\n * itself.\n *\n * Order follows `Object.entries`, which lists integer-like keys first\n * regardless of the order they were written in.\n */\nexport function defineColumnsFromRows<Row>(\n rows: readonly Row[],\n): readonly ColumnDefinition<Row>[] {\n const columns: ColumnDefinition<Row>[] = [];\n const seen = new Set<string>();\n\n function add(field: string, value: unknown): void {\n if (seen.has(field)) {\n return;\n }\n seen.add(field);\n columns.push({\n field,\n type: getType(value),\n alignment: getAlignment(value),\n });\n }\n\n for (const row of rows) {\n if (!isNested(row)) {\n continue;\n }\n for (const [key, value] of Object.entries(row)) {\n if (!isNested(value)) {\n add(key, value);\n continue;\n }\n for (const [nestedKey, nestedValue] of Object.entries(value)) {\n if (!isNested(nestedValue)) {\n add(`${key}.${nestedKey}`, nestedValue);\n }\n }\n }\n }\n\n return columns;\n}\n\n/**\n * The key state about a column is stored under, and the key it renders with.\n * Every such lookup goes through here so the two can never disagree.\n */\nexport function getColumnId<Row>(\n column: ColumnDefinition<Row, unknown>,\n): string {\n return column.id ?? column.field;\n}\n\n/**\n * What a column's header shows: its own `header`, called first if it is a\n * function, or else a label read off the field path — `\"Application.Id\"`\n * reading as `\"Application Id\"`.\n */\nexport function resolveColumnLabel<Row, Header>(\n column: ColumnDefinition<Row, Header>,\n): Header | string {\n const { header } = column;\n if (header === undefined) {\n return column.field.split(\".\").join(\" \");\n }\n // `Header` is open, so it could itself be a function type and the `typeof`\n // check cannot narrow on its own. A header that is callable is the lazy\n // form by definition — no adapter binds `Header` to a function.\n return typeof header === \"function\" ? (header as () => Header)() : header;\n}\n\n/**\n * Accesses a value at a dotted path, e.g. `foo.bar.baz`.\n *\n * @param obj The object to access\n * @param path Path to the value, e.g. `\"foo.bar\"`\n * @returns The value at `path`, or `undefined` if it doesn't exist\n */\nexport function accessDotted(obj: unknown, path: string): unknown {\n return path\n .split(\".\")\n .reduce((value, key) => (isNested(value) ? value[key] : undefined), obj);\n}\n","import type {\n ColumnConstraints,\n ColumnDefinition,\n ColumnResizeSession,\n ColumnResolveOptions,\n ColumnSizeDefaults,\n ColumnSizingState,\n ResolvedColumn,\n} from \"../types\";\nimport { alignmentForType, getColumnId, resolveColumnLabel } from \"./grid\";\n\n/**\n * Applied to any column that does not size itself. The minimum is a width a\n * header stays legible at rather than a measured value — a column dragged\n * narrower than this reads as a rendering fault.\n */\nexport const DEFAULT_COLUMN_SIZES: ColumnSizeDefaults = {\n width: 150,\n minWidth: 40,\n maxWidth: Number.POSITIVE_INFINITY,\n};\n\n/** How far one arrow key press moves a column edge. */\nexport const KEYBOARD_STEP = 10;\n\nfunction withDefaults(\n defaults?: Partial<ColumnSizeDefaults>,\n): ColumnSizeDefaults {\n return { ...DEFAULT_COLUMN_SIZES, ...defaults };\n}\n\n/**\n * The bounds `column` may be sized between. Resolved in one place so that a\n * drag, a clamp and an auto-fit cannot disagree about a column's limits.\n */\nexport function resolveColumnConstraints<Row>(\n column: ColumnDefinition<Row, unknown>,\n defaults?: Partial<ColumnSizeDefaults>,\n): ColumnConstraints {\n const resolved = withDefaults(defaults);\n return {\n minWidth: column.minWidth ?? resolved.minWidth,\n maxWidth: column.maxWidth ?? resolved.maxWidth,\n };\n}\n\n/** Holds `width` within `constraints`. */\nexport function clampColumnWidth(\n width: number,\n constraints: ColumnConstraints,\n): number {\n return Math.min(Math.max(width, constraints.minWidth), constraints.maxWidth);\n}\n\n/**\n * Pairs each column with everything needed to render it — the width it sits\n * at, the label its header shows, whether it may be resized and how its cells\n * align. Widths take the first of the sizing state, the column's own `width`,\n * and the default, then clamp.\n *\n * The sizing state winning means a user's drag outlives a re-render; a column\n * absent from it still tracks a `width` edited in the definition.\n */\nexport function resolveColumnWidths<Row, Header>(\n columns: readonly ColumnDefinition<Row, Header>[],\n sizing: ColumnSizingState,\n options?: ColumnResolveOptions,\n): readonly ResolvedColumn<Row, Header>[] {\n const resolved = withDefaults(options?.sizes);\n\n return columns.map((column) => {\n const id = getColumnId(column);\n const stored = sizing[id];\n\n return {\n column,\n id,\n sized: stored !== undefined,\n width: clampColumnWidth(\n stored ?? column.width ?? resolved.width,\n resolveColumnConstraints(column, resolved),\n ),\n label: resolveColumnLabel(column),\n resizable: column.resizable ?? options?.resizable ?? false,\n alignment: column.alignment ?? alignmentForType(column.type ?? \"string\"),\n };\n });\n}\n\nexport function totalColumnWidth<Row, Header>(\n resolved: readonly ResolvedColumn<Row, Header>[],\n): number {\n return resolved.reduce((total, entry) => total + entry.width, 0);\n}\n\n/**\n * Opens a resize. The session captures its constraints and starting point up\n * front, so applying a pointer position later is arithmetic on numbers alone —\n * which is what keeps the drag testable without a DOM.\n *\n * @param startPosition Where the pointer went down, on the axis being dragged.\n */\nexport function beginColumnResize<Row>(\n column: ColumnDefinition<Row, unknown>,\n startWidth: number,\n startPosition: number,\n defaults?: Partial<ColumnSizeDefaults>,\n): ColumnResizeSession {\n return {\n columnId: getColumnId(column),\n startWidth,\n startPosition,\n constraints: resolveColumnConstraints(column, defaults),\n };\n}\n\n/**\n * The width the dragged column takes with the pointer at `position`. The delta\n * is signed, so a right-to-left adapter negates it and nothing else changes.\n */\nexport function applyColumnResize(\n session: ColumnResizeSession,\n position: number,\n): number {\n return clampColumnWidth(\n session.startWidth + (position - session.startPosition),\n session.constraints,\n );\n}\n\n/**\n * Grows columns proportionally until they fill `availableWidth`.\n *\n * Growth only: columns that already meet or exceed the width are returned\n * untouched, so a container too narrow for them scrolls rather than crushing\n * them. Columns the user has sized are left at that width and the rest share\n * what is left, so dragging one column does not rubber-band it back.\n */\nexport function fitColumnsToWidth<Row, Header>(\n resolved: readonly ResolvedColumn<Row, Header>[],\n availableWidth: number,\n defaults?: Partial<ColumnSizeDefaults>,\n): readonly ResolvedColumn<Row, Header>[] {\n const target = Math.floor(availableWidth);\n if (resolved.length === 0 || totalColumnWidth(resolved) >= target) {\n return resolved;\n }\n\n const entries = resolved.map((entry) => ({\n entry,\n width: entry.width,\n maxWidth: resolveColumnConstraints(entry.column, defaults).maxWidth,\n growable: !entry.sized,\n }));\n const sum = () => entries.reduce((total, item) => total + item.width, 0);\n\n // Clamping a column at its maximum frees surplus the others have to absorb,\n // so a pass that clamped anything is followed by another.\n for (;;) {\n const growing = entries.filter(\n (item) => item.growable && item.width < item.maxWidth,\n );\n const surplus = target - sum();\n if (surplus <= 0 || growing.length === 0) {\n break;\n }\n\n const base = growing.reduce((total, item) => total + item.width, 0);\n let clamped = false;\n\n for (const item of growing) {\n const share =\n base === 0 ? surplus / growing.length : (surplus * item.width) / base;\n\n if (item.width + share >= item.maxWidth) {\n item.width = item.maxWidth;\n clamped = true;\n } else {\n item.width += share;\n }\n }\n\n if (!clamped) {\n break;\n }\n }\n\n // Fractional widths would leave the table a sub-pixel short of its container\n // and show a scrollbar that never goes away, so floor every column and hand\n // the lost pixels back out one at a time.\n let remainder = target - entries.reduce((t, i) => t + Math.floor(i.width), 0);\n\n return entries.map((item) => {\n let width = Math.floor(item.width);\n if (remainder > 0 && item.growable && width < item.maxWidth) {\n width += 1;\n remainder -= 1;\n }\n return { ...item.entry, width };\n });\n}\n\n/**\n * The width a column needs to show `measuredWidth` of content.\n *\n * `padding` is an allowance on top of the measurement, since a column sized to\n * exactly its content can still round down into an ellipsis.\n */\nexport function sizeColumnToContent(\n measuredWidth: number,\n constraints: ColumnConstraints,\n padding = 2,\n): number {\n return clampColumnWidth(Math.ceil(measuredWidth + padding), constraints);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/util/grid.ts","../src/util/sizing.ts"],"sourcesContent":["import type { ColumnAlignment, ColumnDefinition, ColumnType } from \"../types\";\n\n/**\n * Narrows to a value worth drilling into. Mirrors the `LeafValue` union in\n * `../types`: arrays and `Date`s are cell values rather than groups of\n * columns, and `null` cannot be introspected. Keep the two in sync.\n */\nfunction isNested(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\nfunction getType(value: unknown): ColumnType {\n if (value instanceof Date) return \"dateTime\";\n if (typeof value === \"string\") return \"string\";\n if (typeof value === \"number\") return \"number\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"bigint\") return \"number\";\n if (typeof value === \"symbol\") return \"string\";\n return \"string\";\n}\n\n/**\n * How a column of `type` aligns by default. Numeric types align right so that\n * their digits line up by place value; everything else reads from the left.\n */\nexport function alignmentForType(type: ColumnType): ColumnAlignment {\n switch (type) {\n case \"number\":\n case \"decimal\":\n case \"currency\":\n case \"percent\":\n return \"right\";\n default:\n return \"left\";\n }\n}\n\nfunction getAlignment(value: unknown): ColumnAlignment {\n return alignmentForType(getType(value));\n}\n\n/**\n * Derives one column per field across `rows`, in the order each field is first\n * seen — so a key that only appears in a later row still gets a column,\n * appended at the end. Nested objects contribute their non-object properties\n * as `\"Parent.Child\"`; anything deeper is skipped, as is the nested object\n * itself.\n *\n * Order follows `Object.entries`, which lists integer-like keys first\n * regardless of the order they were written in.\n */\nexport function defineColumnsFromRows<Row>(\n rows: readonly Row[],\n): readonly ColumnDefinition<Row>[] {\n const columns: ColumnDefinition<Row>[] = [];\n const seen = new Set<string>();\n\n function add(field: string, value: unknown): void {\n if (seen.has(field)) {\n return;\n }\n seen.add(field);\n columns.push({\n field,\n type: getType(value),\n alignment: getAlignment(value),\n });\n }\n\n for (const row of rows) {\n if (!isNested(row)) {\n continue;\n }\n for (const [key, value] of Object.entries(row)) {\n if (!isNested(value)) {\n add(key, value);\n continue;\n }\n for (const [nestedKey, nestedValue] of Object.entries(value)) {\n if (!isNested(nestedValue)) {\n add(`${key}.${nestedKey}`, nestedValue);\n }\n }\n }\n }\n\n return columns;\n}\n\n/**\n * The key state about a column is stored under, and the key it renders with.\n * Every such lookup goes through here so the two can never disagree.\n */\nexport function getColumnId<Row>(\n column: ColumnDefinition<Row, unknown>,\n): string {\n return column.id ?? column.field;\n}\n\n/**\n * What a column's header shows: its own `headerTemplate`, called first if it is\n * a function, or else a label read off the field path — `\"Application.Id\"`\n * reading as `\"Application Id\"`.\n */\nexport function resolveColumnLabel<Row, Node>(\n column: ColumnDefinition<Row, Node>,\n): Node | string {\n const { headerTemplate } = column;\n if (headerTemplate === undefined) {\n return column.field.split(\".\").join(\" \");\n }\n // `Node` is open, so it could itself be a function type and the `typeof`\n // check cannot narrow on its own. A header that is callable is the lazy\n // form by definition — no adapter binds `Node` to a function.\n return typeof headerTemplate === \"function\"\n ? (headerTemplate as () => Node)()\n : headerTemplate;\n}\n\n/**\n * Accesses a value at a dotted path, e.g. `foo.bar.baz`.\n *\n * @param obj The object to access\n * @param path Path to the value, e.g. `\"foo.bar\"`\n * @returns The value at `path`, or `undefined` if it doesn't exist\n */\nexport function accessDotted(obj: unknown, path: string): unknown {\n return path\n .split(\".\")\n .reduce((value, key) => (isNested(value) ? value[key] : undefined), obj);\n}\n","import type {\n ColumnConstraints,\n ColumnDefinition,\n ColumnResizeSession,\n ColumnResolveOptions,\n ColumnSizeDefaults,\n ColumnSizingState,\n ResolvedColumn,\n} from \"../types\";\nimport { alignmentForType, getColumnId, resolveColumnLabel } from \"./grid\";\n\n/**\n * Applied to any column that does not size itself. The minimum is a width a\n * header stays legible at rather than a measured value — a column dragged\n * narrower than this reads as a rendering fault.\n */\nexport const DEFAULT_COLUMN_SIZES: ColumnSizeDefaults = {\n width: 150,\n minWidth: 40,\n maxWidth: Number.POSITIVE_INFINITY,\n};\n\n/** How far one arrow key press moves a column edge. */\nexport const KEYBOARD_STEP = 10;\n\nfunction withDefaults(\n defaults?: Partial<ColumnSizeDefaults>,\n): ColumnSizeDefaults {\n return { ...DEFAULT_COLUMN_SIZES, ...defaults };\n}\n\n/**\n * The bounds `column` may be sized between. Resolved in one place so that a\n * drag, a clamp and an auto-fit cannot disagree about a column's limits.\n */\nexport function resolveColumnConstraints<Row>(\n column: ColumnDefinition<Row, unknown>,\n defaults?: Partial<ColumnSizeDefaults>,\n): ColumnConstraints {\n const resolved = withDefaults(defaults);\n return {\n minWidth: column.minWidth ?? resolved.minWidth,\n maxWidth: column.maxWidth ?? resolved.maxWidth,\n };\n}\n\n/** Holds `width` within `constraints`. */\nexport function clampColumnWidth(\n width: number,\n constraints: ColumnConstraints,\n): number {\n return Math.min(Math.max(width, constraints.minWidth), constraints.maxWidth);\n}\n\n/**\n * Pairs each column with everything needed to render it — the width it sits\n * at, the label its header shows, whether it may be resized and how its cells\n * align. Widths take the first of the sizing state, the column's own `width`,\n * and the default, then clamp.\n *\n * The sizing state winning means a user's drag outlives a re-render; a column\n * absent from it still tracks a `width` edited in the definition.\n */\nexport function resolveColumnWidths<Row, Node>(\n columns: readonly ColumnDefinition<Row, Node>[],\n sizing: ColumnSizingState,\n options?: ColumnResolveOptions,\n): readonly ResolvedColumn<Row, Node>[] {\n const resolved = withDefaults(options?.sizes);\n\n return columns.map((column) => {\n const id = getColumnId(column);\n const stored = sizing[id];\n\n return {\n column,\n id,\n sized: stored !== undefined,\n width: clampColumnWidth(\n stored ?? column.width ?? resolved.width,\n resolveColumnConstraints(column, resolved),\n ),\n label: resolveColumnLabel(column),\n resizable: column.resizable ?? options?.resizable ?? false,\n alignment: column.alignment ?? alignmentForType(column.type ?? \"string\"),\n };\n });\n}\n\nexport function totalColumnWidth<Row, Node>(\n resolved: readonly ResolvedColumn<Row, Node>[],\n): number {\n return resolved.reduce((total, entry) => total + entry.width, 0);\n}\n\n/**\n * Opens a resize. The session captures its constraints and starting point up\n * front, so applying a pointer position later is arithmetic on numbers alone —\n * which is what keeps the drag testable without a DOM.\n *\n * @param startPosition Where the pointer went down, on the axis being dragged.\n */\nexport function beginColumnResize<Row>(\n column: ColumnDefinition<Row, unknown>,\n startWidth: number,\n startPosition: number,\n defaults?: Partial<ColumnSizeDefaults>,\n): ColumnResizeSession {\n return {\n columnId: getColumnId(column),\n startWidth,\n startPosition,\n constraints: resolveColumnConstraints(column, defaults),\n };\n}\n\n/**\n * The width the dragged column takes with the pointer at `position`. The delta\n * is signed, so a right-to-left adapter negates it and nothing else changes.\n */\nexport function applyColumnResize(\n session: ColumnResizeSession,\n position: number,\n): number {\n return clampColumnWidth(\n session.startWidth + (position - session.startPosition),\n session.constraints,\n );\n}\n\n/**\n * Grows columns proportionally until they fill `availableWidth`.\n *\n * Growth only: columns that already meet or exceed the width are returned\n * untouched, so a container too narrow for them scrolls rather than crushing\n * them. Columns the user has sized are left at that width and the rest share\n * what is left, so dragging one column does not rubber-band it back.\n */\nexport function fitColumnsToWidth<Row, Node>(\n resolved: readonly ResolvedColumn<Row, Node>[],\n availableWidth: number,\n defaults?: Partial<ColumnSizeDefaults>,\n): readonly ResolvedColumn<Row, Node>[] {\n const target = Math.floor(availableWidth);\n if (resolved.length === 0 || totalColumnWidth(resolved) >= target) {\n return resolved;\n }\n\n const entries = resolved.map((entry) => ({\n entry,\n width: entry.width,\n maxWidth: resolveColumnConstraints(entry.column, defaults).maxWidth,\n growable: !entry.sized,\n }));\n const sum = () => entries.reduce((total, item) => total + item.width, 0);\n\n // Clamping a column at its maximum frees surplus the others have to absorb,\n // so a pass that clamped anything is followed by another.\n for (;;) {\n const growing = entries.filter(\n (item) => item.growable && item.width < item.maxWidth,\n );\n const surplus = target - sum();\n if (surplus <= 0 || growing.length === 0) {\n break;\n }\n\n const base = growing.reduce((total, item) => total + item.width, 0);\n let clamped = false;\n\n for (const item of growing) {\n const share =\n base === 0 ? surplus / growing.length : (surplus * item.width) / base;\n\n if (item.width + share >= item.maxWidth) {\n item.width = item.maxWidth;\n clamped = true;\n } else {\n item.width += share;\n }\n }\n\n if (!clamped) {\n break;\n }\n }\n\n // Fractional widths would leave the table a sub-pixel short of its container\n // and show a scrollbar that never goes away, so floor every column and hand\n // the lost pixels back out one at a time.\n let remainder = target - entries.reduce((t, i) => t + Math.floor(i.width), 0);\n\n return entries.map((item) => {\n let width = Math.floor(item.width);\n if (remainder > 0 && item.growable && width < item.maxWidth) {\n width += 1;\n remainder -= 1;\n }\n return { ...item.entry, width };\n });\n}\n\n/**\n * The width a column needs to show `measuredWidth` of content.\n *\n * `padding` is an allowance on top of the measurement, since a column sized to\n * exactly its content can still round down into an ellipsis.\n */\nexport function sizeColumnToContent(\n measuredWidth: number,\n constraints: ColumnConstraints,\n padding = 2,\n): number {\n return clampColumnWidth(Math.ceil(measuredWidth + padding), constraints);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gridkitjs/core",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Framework-agnostic data-grid logic: column resolution, alignment and sizing.",
5
5
  "license": "MIT",
6
6
  "author": "Blagoja Blazhevski",
@@ -22,19 +22,12 @@
22
22
  "engines": {
23
23
  "node": ">=22"
24
24
  },
25
- "main": "./src/index.ts",
26
- "types": "./src/index.ts",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
27
  "exports": {
28
- ".": "./src/index.ts"
29
- },
30
- "publishConfig": {
31
- "main": "./dist/index.js",
32
- "types": "./dist/index.d.ts",
33
- "exports": {
34
- ".": {
35
- "types": "./dist/index.d.ts",
36
- "import": "./dist/index.js"
37
- }
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
38
31
  }
39
32
  },
40
33
  "files": [
@@ -42,15 +35,15 @@
42
35
  "LICENSE",
43
36
  "README.md"
44
37
  ],
38
+ "devDependencies": {
39
+ "tsup": "^8.5.1",
40
+ "typescript": "^6.0.3",
41
+ "vitest": "^4.1.10"
42
+ },
45
43
  "scripts": {
46
44
  "build": "tsup",
47
45
  "typecheck": "tsc --noEmit",
48
46
  "lint": "eslint src",
49
47
  "test": "vitest run"
50
- },
51
- "devDependencies": {
52
- "tsup": "^8.5.1",
53
- "typescript": "^6.0.3",
54
- "vitest": "^4.1.10"
55
48
  }
56
- }
49
+ }