@h6s/table
Advanced tools
Comparing version 1.0.5-alpha.1 to 2.0.0
@@ -1,4 +0,156 @@ | ||
export * from './core/TableCore'; | ||
export { composeDataset } from './helpers/composeDataset'; | ||
export * from './react'; | ||
export * from './types'; | ||
import { PropsWithChildren, ReactNode } from 'react'; | ||
type Primitive = null | undefined | string | number | boolean; | ||
type IsTuple<T extends readonly any[]> = number extends T['length'] ? false : true; | ||
type TupleKey<T extends readonly any[]> = Exclude<keyof T, keyof any[]>; | ||
type ArrayKey = number; | ||
type PathImpl<K extends string | number, V> = V extends Primitive ? `${K}` : `${K}` | `${K}.${Path<V>}`; | ||
type Path<T> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? { | ||
[K in TupleKey<T>]-?: PathImpl<K & string, T[K]>; | ||
}[TupleKey<T>] : PathImpl<ArrayKey, V> : { | ||
[K in keyof T]-?: PathImpl<K & string, T[K]>; | ||
}[keyof T]; | ||
type ArrayPathImpl<K extends string | number, V> = V extends Primitive ? never : V extends ReadonlyArray<infer U> ? U extends Primitive ? never : `${K}` | `${K}.${ArrayPath<V>}` : `${K}.${ArrayPath<V>}`; | ||
type ArrayPath<T> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? { | ||
[K in TupleKey<T>]-?: ArrayPathImpl<K & string, T[K]>; | ||
}[TupleKey<T>] : ArrayPathImpl<ArrayKey, V> : { | ||
[K in keyof T]-?: ArrayPathImpl<K & string, T[K]>; | ||
}[keyof T]; | ||
type PathValue<T, P extends Path<T> | ArrayPath<T>> = T extends any ? P extends `${infer K}.${infer R}` ? K extends keyof T ? R extends Path<T[K]> ? PathValue<T[K], R> : never : K extends `${ArrayKey}` ? T extends ReadonlyArray<infer V> ? PathValue<V, R & Path<V>> : never : never : P extends keyof T ? T[P] : P extends `${ArrayKey}` ? T extends ReadonlyArray<infer V> ? V : never : never : never; | ||
type IteratorWithGeneric<T> = T[]; | ||
type InferGeneric<TargetType> = TargetType extends IteratorWithGeneric<infer InferType> ? InferType : never; | ||
interface ComposeDatasetOptions<Row, Key extends Path<Row>> { | ||
groupBy: Key; | ||
compose: (rows: Array<Omit<Row, Key>>, key: PathValue<Row, Key>) => Array<Omit<Row, Key>>; | ||
} | ||
declare function composeDataset<Row extends Record<string, any>, Key extends Path<Row>>(rows: Row[], { groupBy: key, compose }: ComposeDatasetOptions<Row, Key>): Row[]; | ||
interface CommonCell { | ||
id: string; | ||
rowSpan: number; | ||
colSpan: number; | ||
value: Primitive; | ||
} | ||
interface THead<Row> extends CommonCell { | ||
accessor: Path<Row> | null; | ||
depth: number; | ||
render: CellRecursiveRenderer<THead<Row>>; | ||
labelSequence: string[]; | ||
label: string; | ||
} | ||
interface Cell<Row> extends CommonCell { | ||
accessor: Path<Row>; | ||
rowValues: Row; | ||
render: CellRecursiveRenderer<Cell<Row>>; | ||
labelSequence: string[]; | ||
label: string; | ||
} | ||
interface TFoot<Row> extends CommonCell { | ||
accessor: Path<Row> | null; | ||
render: CellRecursiveRenderer<TFoot<Row>>; | ||
} | ||
interface PrivateAggregatedCell<Row> extends Pick<Renderer<Row>, 'rules'>, Pick<CommonCell, 'id'> { | ||
accessor: Path<Row>; | ||
render: CellRecursiveRenderer<Cell<Row>>; | ||
labelSequence: string[]; | ||
label: string; | ||
} | ||
type CellRendererProps<CellType extends CommonCell> = PropsWithChildren<{ | ||
cellProps: CellType; | ||
}>; | ||
type CellRecursiveRenderer<CellType extends CommonCell> = (props: PropsWithChildren<{ | ||
cellProps: CellType; | ||
}>) => JSX.Element | null; | ||
type CellComponent<CellType extends CommonCell> = (props: { | ||
cellProps: CellType; | ||
}) => ReactNode | Primitive; | ||
interface RendererRules<Row> { | ||
mergeRow?: Path<Row> | Array<Path<Row>> | ((rowValues: Row) => string); | ||
colSpanAs?: number | ((rowValues: Row) => number); | ||
extendsFoot?: boolean; | ||
} | ||
interface Renderer<Row> { | ||
accessor: Path<Row> | Array<Renderer<Row>>; | ||
label: string; | ||
head?: CellComponent<THead<Row>> | Array<CellRecursiveRenderer<THead<Row>>>; | ||
cell?: CellComponent<Cell<Row>> | Array<CellRecursiveRenderer<Cell<Row>>>; | ||
foot?: CellComponent<TFoot<Row>> | Array<CellRecursiveRenderer<TFoot<Row>>>; | ||
rules?: RendererRules<Row>; | ||
} | ||
type RendererModel<Row> = Array<Renderer<Row>>; | ||
type HeadId<Row> = Path<Row>; | ||
type HeadMeta = Record<string, { | ||
label: string; | ||
show: boolean; | ||
countOfChild: number; | ||
countOfParent: number; | ||
}>; | ||
interface RowProps { | ||
id: string; | ||
rowSpan: number; | ||
} | ||
type TableInstance<Row> = { | ||
theadGroups: Array<{ | ||
getRowProps: () => RowProps; | ||
theads: Array<THead<Row>>; | ||
}>; | ||
rows: Array<{ | ||
getRowProps: () => RowProps; | ||
cells: Array<Cell<Row>>; | ||
}>; | ||
tfoots: Array<TFoot<Row>> | null; | ||
headMeta: HeadMeta; | ||
selectableHeadIds: HeadId<Row>[]; | ||
visibleHeadIds: HeadId<Row>[]; | ||
}; | ||
type CommonRenderer<CellType extends CommonCell> = CellComponent<CellType> | Array<CellRecursiveRenderer<CellType>>; | ||
type CellConfig<CellType extends CommonCell> = CommonRenderer<CellType> | Record<string, unknown>; | ||
interface TableColumn<Row> { | ||
accessor: Path<Row> | Array<TableColumn<Row>>; | ||
label: string; | ||
head?: CommonRenderer<THead<Row>> | { | ||
render: CommonRenderer<THead<Row>>; | ||
}; | ||
cell?: CommonRenderer<Cell<Row>> | { | ||
render?: CommonRenderer<Cell<Row>>; | ||
mergeRow?: Path<Row> | Array<Path<Row>> | ((rowValues: Row) => string); | ||
colSpanAs?: number | ((rowValues: Row) => number); | ||
}; | ||
foot?: CommonRenderer<TFoot<Row>> | { | ||
render: CommonRenderer<TFoot<Row>>; | ||
extends?: boolean; | ||
}; | ||
} | ||
type TableModel<Row> = Array<TableColumn<Row>>; | ||
declare function isRenderer<CellType extends CommonCell>(value?: CellConfig<CellType>): value is CommonRenderer<CellType>; | ||
interface Options$1<Row extends Record<string, any>, CellRenderer> { | ||
source?: Row[]; | ||
cellRenderer?: CellRenderer; | ||
defaultHeadIds?: Array<HeadId<Row>>; | ||
} | ||
declare class TableCore<Row extends Record<string, any>, CellRenderer> { | ||
static compose: typeof composeDataset; | ||
private rendererModel; | ||
private headMeta; | ||
private options; | ||
constructor(model: TableModel<Row>, options: Options$1<Row, CellRenderer>); | ||
updateHead(headIds?: Array<HeadId<Row>>): this; | ||
updateSource(source?: Row[]): this; | ||
generate(): TableInstance<Row>; | ||
composeRows<Key extends Path<Row>>(composeOptions: ComposeDatasetOptions<Row, Key>): this; | ||
} | ||
interface Options<Row extends Record<string, any>> { | ||
model: TableModel<Row>; | ||
source?: Row[]; | ||
options?: { | ||
defaultHeadIds?: Array<HeadId<Row>>; | ||
}; | ||
} | ||
declare function useTable<Row extends Record<string, any>>({ model, source, options: { defaultHeadIds }, }: Options<Row>): readonly [TableInstance<Row>, { | ||
updateHead(headIds?: Array<HeadId<Row>>): void; | ||
}]; | ||
export { type ArrayPath, type Cell, type CellComponent, type CellRecursiveRenderer, type CellRendererProps, type CommonCell, type HeadId, type HeadMeta, type InferGeneric, type Path, type PathValue, type Primitive, type PrivateAggregatedCell, type RendererModel, type RendererRules, type TFoot, type THead, TableCore, type TableInstance, type TableModel, composeDataset, isRenderer, useTable }; |
@@ -1,2 +0,636 @@ | ||
var ee=Object.create;var b=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var ne=Object.getPrototypeOf,te=Object.prototype.hasOwnProperty;var B=o=>b(o,"__esModule",{value:!0});var le=(o,e)=>{for(var r in e)b(o,r,{get:e[r],enumerable:!0})},F=(o,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of oe(e))!te.call(o,t)&&(r||t!=="default")&&b(o,t,{get:()=>e[t],enumerable:!(n=re(e,t))||n.enumerable});return o},se=(o,e)=>F(B(b(o!=null?ee(ne(o)):{},"default",!e&&o&&o.__esModule?{get:()=>o.default,enumerable:!0}:{value:o,enumerable:!0})),o),ae=(o=>(e,r)=>o&&o.get(e)||(r=F(B({}),e,1),o&&o.set(e,r),r))(typeof WeakMap!="undefined"?new WeakMap:0);var me={};le(me,{TableCore:()=>h,composeDataset:()=>T,isRenderer:()=>m,useTable:()=>Re});function w(o,e,r=void 0){let n=a=>String.prototype.split.call(e,a).filter(Boolean).reduce((l,s)=>l!=null?l[s]:l,o),t=n(/[,[\]]+?/)||n(/[,[\].]+?/);return t===void 0||t===o?r:t}function g(o,e){return o.reduce((r,n)=>{let t=e(n);return r[t]==null?r[t]=[n]:r[t].push(n),r},{})}function q(o,e){let r=Object.entries(o);return Object.fromEntries(r.map(([n,t])=>[n,e(t)]))}function T(o,{groupBy:e,compose:r}){let n=ie(o,e),t=pe(n,r);return de(t,e)}function ie(o,e){return q(g(o,r=>String(w(r,e))),r=>r.map(({[e]:n,...t})=>t))}function de(o,e){return Object.entries(o).flatMap(([r,n])=>n.map(t=>({[e]:r,...t})))}function pe(o,e){return Object.fromEntries(Object.entries(o).map(([r,n])=>[r,e(n,r)]))}function m(o){return typeof o=="function"||Array.isArray(o)}function S(o){return o.map(e=>({...e,accessor:Array.isArray(e.accessor)?S(e.accessor):e.accessor,head:m(e.head)?e.head:e.head?.render,cell:m(e.cell)?e.cell:e.cell?.render,foot:m(e.foot)?e.foot:e.foot?.render,rules:{mergeRow:m(e.cell)?void 0:e.cell?.mergeRow,colSpanAs:m(e.cell)?void 0:e.cell?.colSpanAs,extendsFoot:m(e.foot)?void 0:e.foot?.extends}}))}function A(o,e=new Error){if(!o)throw typeof e=="string"?new Error(e):e}function v(o){return Object.entries(o)}function y(o){return Array.isArray(o.accessor)?o.accessor.map(e=>y(e)).join("+"):o.accessor}function O(o,e){let r=[];for(let n of o){let t=Array.isArray(n.accessor)?y(n):n.accessor;if(e[t].show){let a=Array.isArray(n.accessor)?{...n,accessor:O(n.accessor,e)}:n;r.push(a)}}return r}function R(){return ue("table")}var H=0,M=new Map;function ue(o){if(M.has(o)){let r=M.get(o)+1;M.set(o,r),H=r}else{let e=1;M.set(o,e),H=e}return`${o}-${H}`}function V(o,e){return E(o,{...e,labelSequence:[]})}function E(o,{cellRenderer:e,labelSequence:r}){let n=[];for(let t of o){let{accessor:a,label:l,cell:s,rules:i}=t,p=Array.isArray(a),c=r.concat(l);p?n.push(...E(a,{labelSequence:c,cellRenderer:e})):n.push({id:R(),accessor:a,label:l,labelSequence:c,render:typeof e=="function"?e(s):({cellProps:d})=>d.value,rules:i})}return n}var P=class{constructor(){this.rowSpanMap=new Map}getColSpan(e,r){let{colSpanRule:n}=this.parseRules(r);return typeof n=="function"?n(e):n??1}getRowSpan(e,r){let n=this.getRowSpanMapKey(e,r);return n==null?1:this.rowSpanMap.get(n)}saveRowSpan(e,r){let n=this.getRowSpanMapKey(e,r);if(n!=null){let t=this.rowSpanMap.get(n);if(t!=null)return this.rowSpanMap.set(n,t+1),!0;this.rowSpanMap.set(n,1)}return!1}getMaxRowSpan(){return Math.max(...this.rowSpanMap.values())}parseRules(e){let{mergeRow:r,colSpanAs:n}=e??{};return{mergeRow:r,colSpanRule:n}}getRowSpanMapKey(e,r){let{mergeRow:n}=this.parseRules(r);if(n!=null)return typeof n=="function"?n(e):Array.isArray(n)?n.map(t=>w(e,t)).join("+"):JSON.stringify(w(e,n))}};function U(o,{cells:e}){let r=new P;return{rows:o.map(a=>e.map(l=>{let s=w(a,l.accessor);return r.saveRowSpan(a,l.rules)?null:{value:s,row:a,...l}}).filter(l=>l!=null)).map(a=>({getRowProps(){return{id:R(),rowSpan:r.getMaxRowSpan()}},cells:a.map(l=>{A(l!=null,"invalid cell");let{row:s,rules:i,value:p,...c}=l;return{rowSpan:r.getRowSpan(s,i)??1,colSpan:r.getColSpan(s,i),rowValues:s,value:p,...c}})}))}}function G(o,e,r){return o.includes(e,r)}function N(o,e){let r=[];for(;o.length>0&&e(o[0]);)r.push(o.shift()??null);return r}function _(o,e){let r=[];for(;o.length>0&&e(o[o.length-1]);)r.push(o.pop()??null);return r}function I(o){let e=[];for(let r of o){let{accessor:n}=r;Array.isArray(n)?e.push(...I(n)):e.push(r)}return e}function z(o,e){let r=ce(o);if(r==null)return{tfoots:null};let{result:{head:n,middle:t,tail:a},colSpanQueue:l}=r,s=L(t,{...e,colSpanQueue:l});return n.length>0&&s.unshift({...x(),colSpan:n.length}),a.length>0&&s.push({...x(),colSpan:a.length}),{tfoots:s.length===0?null:s}}function L(o,{cellRenderer:e,colSpanQueue:r}){let n=[];for(let t of o){let{label:a,accessor:l,foot:s}=t;if(Array.isArray(l))n.push(...L(l,{cellRenderer:e,colSpanQueue:r}));else if(s!=null)n.push({...x(),accessor:l,colSpan:r.shift()??1,value:a,render:typeof e=="function"?e(s):({cellProps:p})=>p.value});else{let p=r.shift();p==null?n.push({...x(),accessor:l,colSpan:1}):r.unshift(p)}}return n}function W(o){let e=[];for(let r of o){let{accessor:n,foot:t,rules:a}=r;if(Array.isArray(n))e.push(...W(n));else if(t!=null)e.push({value:1,extends:a?.extendsFoot??!0});else{let s=e.pop();s!=null?s.extends?e.push({value:(s.value??1)+1,extends:s.extends}):(e.push(s),e.push({value:null,extends:s.extends})):e.push(null)}}return e}function ce(o){let e=I(o);if(e.every(l=>l.foot==null))return null;let r=W(o).map(l=>l?.value??null),n=N(e,l=>l.foot==null),t=_(e,l=>l.foot==null),a=e;if(t.length>0){let l=r.pop()??1;r.push(l-t.length)}return{colSpanQueue:r,result:{head:n,tail:t,middle:a}}}function x(){return{id:R(),accessor:null,rowSpan:1,value:null,render:()=>null}}function C(o){return o.reduce((e,{accessor:r})=>Array.isArray(r)?C(r)+1:e,1)}function K(o,e){return{headMeta:$(o,{...e,depth:0})}}function $(o,e){let r={},{visibleHeadIds:n,depth:t}=e;for(let a of o){let{label:l,accessor:s}=a,i=Array.isArray(s),p=i?s:[{accessor:s}],c=n!=null?p.some(d=>G(n,d.accessor)):!0;r[y(a)]={label:l,show:c,countOfChild:i?C(s):0,countOfParent:t},i&&Object.assign(r,$(s,{...e,depth:t+1}))}return r}function J({theads:o}){let e=Object.values(g(o,n=>String(n.depth)));return{theadGroups:e.map(n=>({theads:n,getRowProps(){return{id:R(),rowSpan:e.length}}}))}}function j(o){return Array.isArray(o.accessor)?o.accessor.reduce((e,r)=>e+j(r),0):1}function Q(o,e){let r=C(o);return X(o,{...e,largestDepth:r,depth:1,labelSequence:[]})}function X(o,{cellRenderer:e,largestDepth:r,depth:n,labelSequence:t}){let a=[];for(let l of o){let{label:s,accessor:i,head:p}=l,c=Array.isArray(i),d=t.concat(s);c&&a.push(...X(i,{largestDepth:r,depth:n+1,labelSequence:d,cellRenderer:e})),a.push({id:R(),accessor:c?null:i,rowSpan:c?1:n>1?r-n+1:r,colSpan:j(l),label:s,value:s,labelSequence:d,render:typeof e=="function"?e(p):({cellProps:Z})=>Z.value,depth:n})}return a}var D=class{constructor(e,r){let n=S(e),{headMeta:t}=K(n,{visibleHeadIds:r.defaultHeadIds});this.options=r,this.rendererModel=n,this.headMeta=t}updateHead(e){A(e==null||e?.length>0,"headIds must be an array");let{headMeta:r}=K(this.rendererModel,{visibleHeadIds:e});return this.headMeta=r,this}updateSource(e){return this.options.source=e,this}generate(){let{rendererModel:e,headMeta:r,options:{source:n=[],cellRenderer:t}}=this,a=O(e,r),{theadGroups:l}=J({theads:Q(a,{cellRenderer:t})}),{rows:s}=U(n,{cells:V(a,{cellRenderer:t})}),{tfoots:i}=z(a,{cellRenderer:t}),p=v(r).filter(([,d])=>d.countOfChild===0).map(([d])=>d),c=v(r).filter(([,d])=>d.show&&d.countOfChild===0).map(([d])=>d);return{theadGroups:l,rows:s,tfoots:i,headMeta:r,selectableHeadIds:p,visibleHeadIds:c}}composeRows(e){return this.options.source=D.compose(this.options.source??[],e),this}},h=D;h.compose=T;var f=require("react");var u=se(require("react"));function Y(o){return({cellProps:e,...r})=>e.colSpan===0?null:o==null||o.length===0?u.default.createElement(u.default.Fragment,null,e.value):typeof o=="string"?u.default.createElement(u.default.Fragment,null,o):typeof o=="function"?u.default.createElement(u.default.Fragment,null,o({cellProps:e,...r})):u.default.createElement(k,{renderers:o,cellProps:e,...r})}function k({renderers:o,index:e=0,cellProps:r}){let n=o[e];return e===o.length-1?e===0?u.default.createElement(n,{cellProps:r,rowSpan:r.rowSpan,colSpan:r.colSpan},r.value):u.default.createElement(n,{cellProps:r},r.value):e===0?u.default.createElement(n,{cellProps:r,rowSpan:r.rowSpan,colSpan:r.colSpan},u.default.createElement(k,{renderers:o,index:e+1,cellProps:r})):u.default.createElement(n,{cellProps:r},u.default.createElement(k,{renderers:o,index:e+1,cellProps:r}))}function Re({model:o,source:e,options:{defaultHeadIds:r}={}}){let n=(0,f.useRef)(new h(o,{source:e,cellRenderer:Y,defaultHeadIds:r})),[t,a]=(0,f.useState)(()=>n.current.generate()),l=(0,f.useMemo)(()=>({updateHead(s){a(()=>n.current.updateHead(s).generate())}}),[]);return(0,f.useEffect)(()=>{a(()=>n.current.updateSource(e).generate())},[e]),[t,l]}module.exports=ae(me); | ||
//# sourceMappingURL=index.js.map | ||
// src/utils/get.ts | ||
function get(obj, path, defaultValue = void 0) { | ||
const travel = (regexp) => String.prototype.split.call(path, regexp).filter(Boolean).reduce((acc, key) => acc !== null && acc !== void 0 ? acc[key] : acc, obj); | ||
const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/); | ||
return result === void 0 || result === obj ? defaultValue : result; | ||
} | ||
// src/utils/groupBy.ts | ||
function groupBy(data, createKey) { | ||
return data.reduce((result, current) => { | ||
const key = createKey(current); | ||
if (result[key] == null) { | ||
result[key] = [current]; | ||
} else { | ||
result[key].push(current); | ||
} | ||
return result; | ||
}, {}); | ||
} | ||
// src/utils/mapValues.ts | ||
function mapValues(value, mapper) { | ||
const entries = Object.entries(value); | ||
return Object.fromEntries( | ||
entries.map(([k, v]) => { | ||
return [k, mapper(v)]; | ||
}) | ||
); | ||
} | ||
// src/helpers/composeDataset.ts | ||
function composeDataset(rows, { groupBy: key, compose }) { | ||
const normalized = normalize(rows, key); | ||
const result = insert(normalized, compose); | ||
return reverseNormalize(result, key); | ||
} | ||
function normalize(rows, key) { | ||
return mapValues( | ||
groupBy(rows, (x) => String(get(x, key))), | ||
(x) => x.map(({ [key]: _, ...rest }) => rest) | ||
); | ||
} | ||
function reverseNormalize(normalizedValue, key) { | ||
return Object.entries(normalizedValue).flatMap( | ||
([id, entries]) => entries.map((entry) => ({ [key]: id, ...entry })) | ||
); | ||
} | ||
function insert(body, compose) { | ||
return Object.fromEntries( | ||
Object.entries(body).map(([key, entities]) => { | ||
return [key, compose(entities, key)]; | ||
}) | ||
); | ||
} | ||
// src/types/table.ts | ||
function isRenderer(value) { | ||
return typeof value === "function" || Array.isArray(value); | ||
} | ||
// src/helpers/transToRendererModel.ts | ||
function transToRendererModel(model) { | ||
return model.map((x) => { | ||
return { | ||
...x, | ||
accessor: Array.isArray(x.accessor) ? transToRendererModel(x.accessor) : x.accessor, | ||
head: isRenderer(x.head) ? x.head : x.head?.render, | ||
cell: isRenderer(x.cell) ? x.cell : x.cell?.render, | ||
foot: isRenderer(x.foot) ? x.foot : x.foot?.render, | ||
rules: { | ||
mergeRow: isRenderer(x.cell) ? void 0 : x.cell?.mergeRow, | ||
colSpanAs: isRenderer(x.cell) ? void 0 : x.cell?.colSpanAs, | ||
extendsFoot: isRenderer(x.foot) ? void 0 : x.foot?.extends | ||
} | ||
}; | ||
}); | ||
} | ||
// src/utils/invariant.ts | ||
function invariant(condition, error = new Error()) { | ||
if (!condition) { | ||
if (typeof error === "string") { | ||
throw new Error(error); | ||
} else { | ||
throw error; | ||
} | ||
} | ||
} | ||
// src/utils/object.ts | ||
function objectEntries(obj) { | ||
return Object.entries(obj); | ||
} | ||
// src/core/renderer/getHeaderAccessorId.ts | ||
function getHeaderAccessorId(model) { | ||
return Array.isArray(model.accessor) ? model.accessor.map((model2) => getHeaderAccessorId(model2)).join("+") : model.accessor; | ||
} | ||
// src/core/renderer/buildRendererModel.ts | ||
function buildRendererModel(rendererModel, headMeta) { | ||
const result = []; | ||
for (const model of rendererModel) { | ||
const id = Array.isArray(model.accessor) ? getHeaderAccessorId(model) : model.accessor; | ||
if (headMeta[id].show) { | ||
const value = Array.isArray(model.accessor) ? { ...model, accessor: buildRendererModel(model.accessor, headMeta) } : model; | ||
result.push(value); | ||
} | ||
} | ||
return result; | ||
} | ||
// src/utils/generateTableID.ts | ||
function generateTableID() { | ||
return generateID("table"); | ||
} | ||
var randomId = 0; | ||
var map = /* @__PURE__ */ new Map(); | ||
function generateID(prefix) { | ||
if (map.has(prefix)) { | ||
const id = map.get(prefix); | ||
const newId = id + 1; | ||
map.set(prefix, newId); | ||
randomId = newId; | ||
} else { | ||
const id = 1; | ||
map.set(prefix, id); | ||
randomId = id; | ||
} | ||
return `${prefix}-${randomId}`; | ||
} | ||
// src/core/row/buildCells.ts | ||
function buildCells(rendererModel, options) { | ||
return _build(rendererModel, { ...options, labelSequence: [] }); | ||
} | ||
function _build(rendererModel, { cellRenderer: cellRenderer2, labelSequence }) { | ||
const cells = []; | ||
for (const model of rendererModel) { | ||
const { accessor, label, cell, rules } = model; | ||
const hasChild = Array.isArray(accessor); | ||
const sequence = labelSequence.concat(label); | ||
if (hasChild) { | ||
cells.push( | ||
..._build(accessor, { | ||
labelSequence: sequence, | ||
cellRenderer: cellRenderer2 | ||
}) | ||
); | ||
} else { | ||
cells.push({ | ||
id: generateTableID(), | ||
accessor, | ||
label, | ||
labelSequence: sequence, | ||
render: typeof cellRenderer2 === "function" ? cellRenderer2(cell) : ({ cellProps }) => cellProps.value, | ||
rules | ||
}); | ||
} | ||
} | ||
return cells; | ||
} | ||
// src/core/row/CellSpanManager.ts | ||
var CellSpanManager = class { | ||
rowSpanMap; | ||
constructor() { | ||
this.rowSpanMap = /* @__PURE__ */ new Map(); | ||
} | ||
getColSpan(Row, rules) { | ||
const { colSpanRule } = this.parseRules(rules); | ||
return typeof colSpanRule === "function" ? colSpanRule(Row) : colSpanRule ?? 1; | ||
} | ||
getRowSpan(Row, rules) { | ||
const key = this.getRowSpanMapKey(Row, rules); | ||
if (key == null) { | ||
return 1; | ||
} | ||
return this.rowSpanMap.get(key); | ||
} | ||
saveRowSpan(Row, rules) { | ||
const key = this.getRowSpanMapKey(Row, rules); | ||
if (key != null) { | ||
const savedRowSpan = this.rowSpanMap.get(key); | ||
if (savedRowSpan != null) { | ||
this.rowSpanMap.set(key, savedRowSpan + 1); | ||
return true; | ||
} | ||
this.rowSpanMap.set(key, 1); | ||
} | ||
return false; | ||
} | ||
getMaxRowSpan() { | ||
return Math.max(...this.rowSpanMap.values()); | ||
} | ||
parseRules(rules) { | ||
const { mergeRow, colSpanAs: colSpanRule } = rules ?? {}; | ||
return { mergeRow, colSpanRule }; | ||
} | ||
getRowSpanMapKey(row, rules) { | ||
const { mergeRow } = this.parseRules(rules); | ||
if (mergeRow == null) { | ||
return; | ||
} | ||
if (typeof mergeRow === "function") { | ||
return mergeRow(row); | ||
} | ||
if (Array.isArray(mergeRow)) { | ||
return mergeRow.map((accessor) => get(row, accessor)).join("+"); | ||
} | ||
return JSON.stringify(get(row, mergeRow)); | ||
} | ||
}; | ||
// src/core/row/buildRows.ts | ||
function buildRows(data, { cells }) { | ||
const manager = new CellSpanManager(); | ||
const candidateRows = data.map((row) => { | ||
return cells.map((cell) => { | ||
const value = get(row, cell.accessor); | ||
const dropCell = manager.saveRowSpan(row, cell.rules); | ||
if (dropCell) { | ||
return null; | ||
} | ||
return { value, row, ...cell }; | ||
}).filter((x) => x != null); | ||
}); | ||
const rows = candidateRows.map((cells2) => { | ||
return { | ||
getRowProps() { | ||
return { | ||
id: generateTableID(), | ||
rowSpan: manager.getMaxRowSpan() | ||
}; | ||
}, | ||
cells: cells2.map((aggregatedCell) => { | ||
invariant(aggregatedCell != null, "invalid cell"); | ||
const { row, rules, value, ...rest } = aggregatedCell; | ||
const cell = { | ||
rowSpan: manager.getRowSpan(row, rules) ?? 1, | ||
colSpan: manager.getColSpan(row, rules), | ||
rowValues: row, | ||
value, | ||
...rest | ||
}; | ||
return cell; | ||
}) | ||
}; | ||
}); | ||
return { rows }; | ||
} | ||
// src/utils/array.ts | ||
function arrayIncludes(array, item, fromIndex) { | ||
return array.includes(item, fromIndex); | ||
} | ||
function shiftUntil(arr, predicate) { | ||
const result = []; | ||
while (arr.length > 0 && predicate(arr[0])) { | ||
result.push(arr.shift() ?? null); | ||
} | ||
return result; | ||
} | ||
function popUntil(arr, predicate) { | ||
const result = []; | ||
while (arr.length > 0 && predicate(arr[arr.length - 1])) { | ||
result.push(arr.pop() ?? null); | ||
} | ||
return result; | ||
} | ||
// src/core/renderer/flattenRendererModel.ts | ||
function flattenRendererModel(rendererModel) { | ||
const models = []; | ||
for (const model of rendererModel) { | ||
const { accessor } = model; | ||
const hasChild = Array.isArray(accessor); | ||
if (hasChild) { | ||
models.push(...flattenRendererModel(accessor)); | ||
} else { | ||
models.push(model); | ||
} | ||
} | ||
return models; | ||
} | ||
// src/core/tfoot/buildTFoots.ts | ||
function buildTFoots(rendererModel, options) { | ||
const parsed = prepare(rendererModel); | ||
if (parsed == null) { | ||
return { tfoots: null }; | ||
} | ||
const { | ||
result: { head, middle, tail }, | ||
colSpanQueue | ||
} = parsed; | ||
const tfoots = _build2(middle, { ...options, colSpanQueue }); | ||
if (head.length > 0) { | ||
tfoots.unshift({ ...getDefaultTFootCell(), colSpan: head.length }); | ||
} | ||
if (tail.length > 0) { | ||
tfoots.push({ ...getDefaultTFootCell(), colSpan: tail.length }); | ||
} | ||
return { tfoots: tfoots.length === 0 ? null : tfoots }; | ||
} | ||
function _build2(rendererModel, { cellRenderer: cellRenderer2, colSpanQueue }) { | ||
const tfoots = []; | ||
for (const model of rendererModel) { | ||
const { label, accessor, foot } = model; | ||
const hasChild = Array.isArray(accessor); | ||
if (hasChild) { | ||
tfoots.push(..._build2(accessor, { cellRenderer: cellRenderer2, colSpanQueue })); | ||
} else { | ||
if (foot != null) { | ||
tfoots.push({ | ||
...getDefaultTFootCell(), | ||
accessor, | ||
colSpan: colSpanQueue.shift() ?? 1, | ||
value: label, | ||
render: typeof cellRenderer2 === "function" ? cellRenderer2(foot) : ({ cellProps }) => cellProps.value | ||
}); | ||
} else { | ||
const colSpan = colSpanQueue.shift(); | ||
if (colSpan == null) { | ||
tfoots.push({ | ||
...getDefaultTFootCell(), | ||
accessor, | ||
colSpan: 1 | ||
}); | ||
} else { | ||
colSpanQueue.unshift(colSpan); | ||
} | ||
} | ||
} | ||
} | ||
return tfoots; | ||
} | ||
function buildColSpanQueue(rendererModel) { | ||
const queue = []; | ||
for (const model of rendererModel) { | ||
const { accessor, foot, rules } = model; | ||
const hasChild = Array.isArray(accessor); | ||
if (hasChild) { | ||
queue.push(...buildColSpanQueue(accessor)); | ||
} else { | ||
if (foot != null) { | ||
queue.push({ | ||
value: 1, | ||
extends: rules?.extendsFoot ?? true | ||
}); | ||
} else { | ||
const lastValue = queue.pop(); | ||
if (lastValue != null) { | ||
if (lastValue.extends) { | ||
queue.push({ value: (lastValue.value ?? 1) + 1, extends: lastValue.extends }); | ||
} else { | ||
queue.push(lastValue); | ||
queue.push({ value: null, extends: lastValue.extends }); | ||
} | ||
} else { | ||
queue.push(null); | ||
} | ||
} | ||
} | ||
} | ||
return queue; | ||
} | ||
function prepare(rendererModel) { | ||
const model = flattenRendererModel(rendererModel); | ||
if (model.every((x) => x.foot == null)) { | ||
return null; | ||
} | ||
const colSpanQueue = buildColSpanQueue(rendererModel).map((x) => x?.value ?? null); | ||
const head = shiftUntil(model, (x) => x.foot == null); | ||
const tail = popUntil(model, (x) => x.foot == null); | ||
const middle = model; | ||
if (tail.length > 0) { | ||
const lastColSpan = colSpanQueue.pop() ?? 1; | ||
colSpanQueue.push(lastColSpan - tail.length); | ||
} | ||
return { | ||
colSpanQueue, | ||
result: { | ||
head, | ||
tail, | ||
middle | ||
} | ||
}; | ||
} | ||
function getDefaultTFootCell() { | ||
return { | ||
id: generateTableID(), | ||
accessor: null, | ||
rowSpan: 1, | ||
value: null, | ||
render: () => null | ||
}; | ||
} | ||
// src/core/renderer/getLargestDepth.ts | ||
function getLargestDepth(rendererModel) { | ||
return rendererModel.reduce((acc, { accessor }) => { | ||
return Array.isArray(accessor) ? getLargestDepth(accessor) + 1 : acc; | ||
}, 1); | ||
} | ||
// src/core/thead/buildHeadMeta.ts | ||
function buildHeadMeta(rendererModel, options) { | ||
const headMeta = _build3(rendererModel, { ...options, depth: 0 }); | ||
return { headMeta }; | ||
} | ||
function _build3(rendererModel, options) { | ||
const headMeta = {}; | ||
const { visibleHeadIds, depth } = options; | ||
for (const model of rendererModel) { | ||
const { label, accessor } = model; | ||
const hasChild = Array.isArray(accessor); | ||
const base = hasChild ? accessor : [{ accessor }]; | ||
const show = visibleHeadIds != null ? base.some((x) => arrayIncludes(visibleHeadIds, x.accessor)) : true; | ||
headMeta[getHeaderAccessorId(model)] = { | ||
label, | ||
show, | ||
countOfChild: hasChild ? getLargestDepth(accessor) : 0, | ||
countOfParent: depth | ||
}; | ||
if (hasChild) { | ||
Object.assign(headMeta, _build3(accessor, { ...options, depth: depth + 1 })); | ||
} | ||
} | ||
return headMeta; | ||
} | ||
// src/core/thead/buildTHeadGroups.ts | ||
function buildTHeadGroups({ theads }) { | ||
const groupByDepth = Object.values(groupBy(theads, (x) => String(x.depth))); | ||
const theadGroups = groupByDepth.map((theads2) => { | ||
return { | ||
theads: theads2, | ||
getRowProps() { | ||
return { | ||
id: generateTableID(), | ||
rowSpan: groupByDepth.length | ||
}; | ||
} | ||
}; | ||
}); | ||
return { theadGroups }; | ||
} | ||
// src/core/renderer/getChildrenCount.ts | ||
function getChildrenCount(model) { | ||
if (!Array.isArray(model.accessor)) { | ||
return 1; | ||
} | ||
return model.accessor.reduce((acc, result) => { | ||
return acc + getChildrenCount(result); | ||
}, 0); | ||
} | ||
// src/core/thead/buildTHeads.ts | ||
function buildTHeads(rendererModel, options) { | ||
const largestDepth = getLargestDepth(rendererModel); | ||
return _build4(rendererModel, { ...options, largestDepth, depth: 1, labelSequence: [] }); | ||
} | ||
function _build4(rendererModel, { cellRenderer: cellRenderer2, largestDepth, depth, labelSequence }) { | ||
const heads = []; | ||
for (const model of rendererModel) { | ||
const { label, accessor, head } = model; | ||
const hasChild = Array.isArray(accessor); | ||
const sequence = labelSequence.concat(label); | ||
if (hasChild) { | ||
heads.push( | ||
..._build4(accessor, { | ||
largestDepth, | ||
depth: depth + 1, | ||
labelSequence: sequence, | ||
cellRenderer: cellRenderer2 | ||
}) | ||
); | ||
} | ||
heads.push({ | ||
id: generateTableID(), | ||
accessor: hasChild ? null : accessor, | ||
rowSpan: hasChild ? 1 : depth > 1 ? largestDepth - depth + 1 : largestDepth, | ||
colSpan: getChildrenCount(model), | ||
label, | ||
value: label, | ||
labelSequence: sequence, | ||
render: typeof cellRenderer2 === "function" ? cellRenderer2(head) : ({ cellProps }) => cellProps.value, | ||
depth | ||
}); | ||
} | ||
return heads; | ||
} | ||
// src/core/TableCore.ts | ||
var TableCore = class _TableCore { | ||
static compose = composeDataset; | ||
rendererModel; | ||
headMeta; | ||
options; | ||
constructor(model, options) { | ||
const rendererModel = transToRendererModel(model); | ||
const { headMeta } = buildHeadMeta(rendererModel, { | ||
visibleHeadIds: options.defaultHeadIds | ||
}); | ||
this.options = options; | ||
this.rendererModel = rendererModel; | ||
this.headMeta = headMeta; | ||
} | ||
updateHead(headIds) { | ||
invariant(headIds == null || headIds?.length > 0, "headIds must be an array"); | ||
const { headMeta } = buildHeadMeta(this.rendererModel, { | ||
visibleHeadIds: headIds | ||
}); | ||
this.headMeta = headMeta; | ||
return this; | ||
} | ||
updateSource(source) { | ||
this.options.source = source; | ||
return this; | ||
} | ||
generate() { | ||
const { | ||
rendererModel, | ||
headMeta, | ||
options: { source = [], cellRenderer: cellRenderer2 } | ||
} = this; | ||
const model = buildRendererModel(rendererModel, headMeta); | ||
const { theadGroups } = buildTHeadGroups({ | ||
theads: buildTHeads(model, { cellRenderer: cellRenderer2 }) | ||
}); | ||
const { rows } = buildRows(source, { | ||
cells: buildCells(model, { cellRenderer: cellRenderer2 }) | ||
}); | ||
const { tfoots } = buildTFoots(model, { cellRenderer: cellRenderer2 }); | ||
const selectableHeadIds = objectEntries(headMeta).filter(([, x]) => x.countOfChild === 0).map(([id]) => id); | ||
const visibleHeadIds = objectEntries(headMeta).filter(([, x]) => x.show && x.countOfChild === 0).map(([id]) => id); | ||
return { | ||
theadGroups, | ||
rows, | ||
tfoots, | ||
headMeta, | ||
selectableHeadIds, | ||
visibleHeadIds | ||
}; | ||
} | ||
composeRows(composeOptions) { | ||
this.options.source = _TableCore.compose(this.options.source ?? [], composeOptions); | ||
return this; | ||
} | ||
}; | ||
// src/react/useTable.ts | ||
import { useEffect, useMemo, useRef, useState } from "react"; | ||
// src/helpers/cellRenderer.tsx | ||
import { Fragment, jsx } from "react/jsx-runtime"; | ||
function cellRenderer(renderers) { | ||
return ({ cellProps, ...props }) => { | ||
if (cellProps.colSpan === 0) { | ||
return null; | ||
} | ||
if (renderers == null || renderers.length === 0) { | ||
return /* @__PURE__ */ jsx(Fragment, { children: cellProps.value }); | ||
} | ||
if (typeof renderers === "string") { | ||
return /* @__PURE__ */ jsx(Fragment, { children: renderers }); | ||
} | ||
if (typeof renderers === "function") { | ||
return /* @__PURE__ */ jsx(Fragment, { children: renderers({ cellProps, ...props }) }); | ||
} | ||
return /* @__PURE__ */ jsx(CombinedCell, { renderers, cellProps, ...props }); | ||
}; | ||
} | ||
function CombinedCell({ | ||
renderers, | ||
index = 0, | ||
cellProps | ||
}) { | ||
const Renderer = renderers[index]; | ||
if (index === renderers.length - 1) { | ||
if (index === 0) { | ||
const CellComponent = Renderer; | ||
return /* @__PURE__ */ jsx( | ||
CellComponent, | ||
{ | ||
cellProps, | ||
rowSpan: cellProps.rowSpan, | ||
colSpan: cellProps.colSpan, | ||
children: cellProps.value | ||
} | ||
); | ||
} | ||
return /* @__PURE__ */ jsx(Renderer, { cellProps, children: cellProps.value }); | ||
} | ||
if (index === 0) { | ||
const CellComponent = Renderer; | ||
return /* @__PURE__ */ jsx(CellComponent, { cellProps, rowSpan: cellProps.rowSpan, colSpan: cellProps.colSpan, children: /* @__PURE__ */ jsx(CombinedCell, { renderers, index: index + 1, cellProps }) }); | ||
} | ||
return /* @__PURE__ */ jsx(Renderer, { cellProps, children: /* @__PURE__ */ jsx(CombinedCell, { renderers, index: index + 1, cellProps }) }); | ||
} | ||
// src/react/useTable.ts | ||
function useTable({ | ||
model, | ||
source, | ||
options: { defaultHeadIds } = {} | ||
}) { | ||
const tableCore = useRef( | ||
new TableCore(model, { | ||
source, | ||
cellRenderer, | ||
defaultHeadIds | ||
}) | ||
); | ||
const [instance, setInstance] = useState(() => tableCore.current.generate()); | ||
const controls = useMemo( | ||
() => ({ | ||
updateHead(headIds) { | ||
setInstance(() => tableCore.current.updateHead(headIds).generate()); | ||
} | ||
}), | ||
[] | ||
); | ||
useEffect(() => { | ||
setInstance(() => tableCore.current.updateSource(source).generate()); | ||
}, [source]); | ||
return [instance, controls]; | ||
} | ||
export { | ||
TableCore, | ||
composeDataset, | ||
isRenderer, | ||
useTable | ||
}; |
{ | ||
"name": "@h6s/table", | ||
"version": "1.0.5-alpha.1", | ||
"version": "2.0.0", | ||
"sideEffects": false, | ||
"main": "src/index.ts", | ||
"files": [ | ||
"dist", | ||
"esm" | ||
], | ||
"exports": { | ||
".": { | ||
"require": "./src/index.ts", | ||
"import": "./src/index.ts" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"type": "module", | ||
"publishConfig": { | ||
"access": "public", | ||
"main": "dist/index.cjs", | ||
"module": "dist/index.js", | ||
"types": "dist/index.d.cts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"require": "./dist/index.js", | ||
"import": "./esm/index.mjs" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"import": "esm/index.mjs", | ||
"module": "esm/index.mjs" | ||
"require": { | ||
"types": "./dist/index.d.cts", | ||
"default": "./dist/index.cjs" | ||
}, | ||
"import": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
} | ||
} | ||
} | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"prepack": "yarn build", | ||
"prebuild": "rimraf dist esm", | ||
"build:type": "yarn run -T tsc --emitDeclarationOnly --declaration", | ||
"build": "yarn build:type && node ../../scripts/build.js", | ||
"build": "yarn run -T tsup", | ||
"lint": "yarn run -T eslint 'src/**/*.{js,jsx,ts,tsx}'", | ||
"lint:fix": "yarn lint --fix", | ||
"typecheck": "yarn run -T tsc", | ||
"test": "yarn run -T jest --config ../../jest.config.js --roots './packages/table/src'", | ||
"test:playwright": "playwright test", | ||
"test": "vitest", | ||
"test:cov": "yarn test --coverage", | ||
"test:watch": "yarn test --watch", | ||
"semantic-release": "semantic-release" | ||
"test:watch": "yarn test --watch" | ||
}, | ||
"devDependencies": { | ||
"@playwright/test": "1.17.1", | ||
"@storybook/react": "6.4.9", | ||
"@testing-library/react-hooks": "7.0.2", | ||
"@types/jest": "27.4.0", | ||
"@types/node": "17.0.5", | ||
"@types/react": "^18.0.25", | ||
"playwright": "1.17.1", | ||
"@storybook/react": "7.6.4", | ||
"@testing-library/jest-dom": "^6.1.5", | ||
"@testing-library/react-hooks": "8.0.1", | ||
"@types/node": "20.10.4", | ||
"@types/react": "^18.2.43", | ||
"happy-dom": "^12.10.3", | ||
"jsdom": "^23.0.1", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-test-renderer": "^18.2.0" | ||
"react-test-renderer": "^18.2.0", | ||
"typescript": "^5.3.3", | ||
"vite": "^5.0.7", | ||
"vitest": "^1.0.4" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.8 || ^17.0 || ^18.0" | ||
"react": ">= 18" | ||
}, | ||
@@ -61,0 +55,0 @@ "peerDependenciesMeta": { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1406
1
Yes
52949
13
7
1