@operato/data-grist 1.11.6 → 1.11.7

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 (36) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/demo/data-grist-test.html +2 -1
  3. package/dist/src/configure/column-builder.js +8 -0
  4. package/dist/src/configure/column-builder.js.map +1 -1
  5. package/dist/src/data-grid/data-grid-accum-field.js +2 -15
  6. package/dist/src/data-grid/data-grid-accum-field.js.map +1 -1
  7. package/dist/src/data-grid/data-grid-header.d.ts +8 -5
  8. package/dist/src/data-grid/data-grid-header.js +131 -47
  9. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  10. package/dist/src/renderers/ox-grist-renderer-tree.d.ts +8 -0
  11. package/dist/src/renderers/ox-grist-renderer-tree.js +134 -0
  12. package/dist/src/renderers/ox-grist-renderer-tree.js.map +1 -0
  13. package/dist/src/types.d.ts +2 -0
  14. package/dist/src/types.js.map +1 -1
  15. package/dist/stories/fixed-column.stories copy.d.ts +26 -0
  16. package/dist/stories/fixed-column.stories copy.js +448 -0
  17. package/dist/stories/fixed-column.stories copy.js.map +1 -0
  18. package/dist/stories/fixed-column.stories.js +2 -1
  19. package/dist/stories/fixed-column.stories.js.map +1 -1
  20. package/dist/stories/grist-modes.stories.js +2 -1
  21. package/dist/stories/grist-modes.stories.js.map +1 -1
  22. package/dist/stories/group-header.stories.d.ts +26 -0
  23. package/dist/stories/group-header.stories.js +473 -0
  24. package/dist/stories/group-header.stories.js.map +1 -0
  25. package/dist/stories/tree-column.stories.d.ts +26 -0
  26. package/dist/stories/tree-column.stories.js +310 -0
  27. package/dist/stories/tree-column.stories.js.map +1 -0
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +2 -2
  30. package/src/configure/column-builder.ts +8 -0
  31. package/src/data-grid/data-grid-accum-field.ts +2 -15
  32. package/src/data-grid/data-grid-header.ts +148 -47
  33. package/src/types.ts +2 -0
  34. package/stories/fixed-column.stories.ts +2 -1
  35. package/stories/grist-modes.stories.ts +2 -1
  36. package/stories/group-header.stories.ts +505 -0
@@ -0,0 +1,134 @@
1
+ import { __decorate } from "tslib";
2
+ import { css, html } from 'lit';
3
+ import { customElement } from 'lit/decorators.js';
4
+ import { OxGristRenderer } from './ox-grist-renderer';
5
+ let OxGristRendererTree = class OxGristRendererTree extends OxGristRenderer {
6
+ get rendererTemplate() {
7
+ var { childrenProperty = 'children' } = this.column.record.options || {};
8
+ var { __parent__: parent, __children__: children, __collapsed__: collapsed = true } = this.record;
9
+ const expandable = childrenProperty in this.record;
10
+ return html `
11
+ ${expandable
12
+ ? html ` <span expander @click=${this.onClickExpander.bind(this)} ?collapsed=${collapsed}></span> `
13
+ : html ``}
14
+ <span checkbox></span>
15
+ <span label>${this.value}</span>
16
+ `;
17
+ }
18
+ updated(changes) {
19
+ var { __depth__ } = this.record;
20
+ this.style.setProperty('--tree-depth', String(__depth__));
21
+ }
22
+ onClickExpander(e) {
23
+ e.stopPropagation();
24
+ this.record.__collapsed__ = !this.record.__collapsed__;
25
+ this.requestUpdate();
26
+ }
27
+ };
28
+ OxGristRendererTree.styles = css `
29
+ :host {
30
+ position: relative;
31
+
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 6px;
35
+
36
+ width: 100%;
37
+ padding-left: calc(var(--tree-depth, 0) * 18px);
38
+
39
+ cursor: pointer;
40
+ }
41
+
42
+ span[expander] {
43
+ display: inline-block;
44
+ vertical-align: middle;
45
+ width: 12px;
46
+ height: 20px;
47
+ cursor: pointer;
48
+ position: relative;
49
+ }
50
+
51
+ span[expander]::before {
52
+ position: absolute;
53
+ top: 50%;
54
+ left: 50%;
55
+ transform: translate(-50%, -25%);
56
+ content: ' ';
57
+ border: 5px solid transparent;
58
+ border-top: 5px solid var(--primary-color, #1890ff);
59
+ }
60
+
61
+ span[expander][collapsed]::before {
62
+ transform: translate(-25%, -50%) rotate(-90deg);
63
+ }
64
+
65
+ span[checkbox] {
66
+ display: inline-block;
67
+ vertical-align: middle;
68
+ width: 12px;
69
+ height: 20px;
70
+ cursor: pointer;
71
+ position: relative;
72
+ }
73
+
74
+ span[checkbox]::before {
75
+ cursor: pointer;
76
+ position: absolute;
77
+ top: 50%;
78
+ left: 50%;
79
+ transform: translate(-50%, -50%);
80
+ content: ' ';
81
+ display: block;
82
+ width: 10px;
83
+ height: 10px;
84
+ border: 1px solid var(--primary-color, #1890ff);
85
+ border-radius: 2px;
86
+ }
87
+
88
+ span[label][selected] {
89
+ background-color: var(--primary-color, #1890ff);
90
+ }
91
+
92
+ li[checked='checked'] > div[self] > span[checkbox]::before {
93
+ background-color: var(--primary-color, #1890ff);
94
+ border-color: var(--primary-color, #1890ff);
95
+ }
96
+
97
+ li[checked='checked'] > div[self] > span[checkbox]::after {
98
+ position: absolute;
99
+ content: ' ';
100
+ display: block;
101
+ top: 50%;
102
+ left: 50%;
103
+ width: 3px;
104
+ height: 7px;
105
+ border: 2px solid #fff;
106
+ border-top: none;
107
+ border-left: none;
108
+ -webkit-transform: translate(-50%, -50%) rotate(45deg);
109
+ -ms-transform: translate(-50%, -50%) rotate(45deg);
110
+ transform: translate(-50%, -50%) rotate(45deg);
111
+ }
112
+
113
+ li[checked='half-checked'] > div[self] > span[checkbox]::before {
114
+ background-color: var(--primary-color, #1890ff);
115
+ border-color: var(--primary-color, #1890ff);
116
+ }
117
+
118
+ li[checked='half-checked'] > div[self] > span[checkbox]::after {
119
+ position: absolute;
120
+ content: ' ';
121
+ display: block;
122
+ top: 50%;
123
+ left: 50%;
124
+ transform: translate(-50%, -50%);
125
+ width: 10px;
126
+ height: 2px;
127
+ background-color: #fff;
128
+ }
129
+ `;
130
+ OxGristRendererTree = __decorate([
131
+ customElement('ox-grist-tree-renderer')
132
+ ], OxGristRendererTree);
133
+ export { OxGristRendererTree };
134
+ //# sourceMappingURL=ox-grist-renderer-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ox-grist-renderer-tree.js","sourceRoot":"","sources":["../../../src/renderers/ox-grist-renderer-tree.ts"],"names":[],"mappings":";AAAA,OAAO,EAAkB,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAY,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAG9C,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,eAAe;IAwGtD,IAAI,gBAAgB;QAClB,IAAI,EAAE,gBAAgB,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAA;QACxE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QAEjG,MAAM,UAAU,GAAG,gBAAgB,IAAI,IAAI,CAAC,MAAM,CAAA;QAElD,OAAO,IAAI,CAAA;QACP,UAAU;YACV,CAAC,CAAC,IAAI,CAAA,0BAA0B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,SAAS,WAAW;YAClG,CAAC,CAAC,IAAI,CAAA,EAAE;;oBAEI,IAAI,CAAC,KAAK;KACzB,CAAA;IACH,CAAC;IAED,OAAO,CAAC,OAA6B;QACnC,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QAE/B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;IAC3D,CAAC;IAED,eAAe,CAAC,CAAa;QAC3B,CAAC,CAAC,eAAe,EAAE,CAAA;QAEnB,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAA;QAEtD,IAAI,CAAC,aAAa,EAAE,CAAA;IACtB,CAAC;;AAlIM,0BAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGlB,AArGY,CAqGZ;AAtGU,mBAAmB;IAD/B,aAAa,CAAC,wBAAwB,CAAC;GAC3B,mBAAmB,CA4L/B","sourcesContent":["import { PropertyValues, css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { OxGristRenderer } from './ox-grist-renderer'\n\n@customElement('ox-grist-tree-renderer')\nexport class OxGristRendererTree extends OxGristRenderer {\n static styles = css`\n :host {\n position: relative;\n\n display: flex;\n align-items: center;\n gap: 6px;\n\n width: 100%;\n padding-left: calc(var(--tree-depth, 0) * 18px);\n\n cursor: pointer;\n }\n\n span[expander] {\n display: inline-block;\n vertical-align: middle;\n width: 12px;\n height: 20px;\n cursor: pointer;\n position: relative;\n }\n\n span[expander]::before {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -25%);\n content: ' ';\n border: 5px solid transparent;\n border-top: 5px solid var(--primary-color, #1890ff);\n }\n\n span[expander][collapsed]::before {\n transform: translate(-25%, -50%) rotate(-90deg);\n }\n\n span[checkbox] {\n display: inline-block;\n vertical-align: middle;\n width: 12px;\n height: 20px;\n cursor: pointer;\n position: relative;\n }\n\n span[checkbox]::before {\n cursor: pointer;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n content: ' ';\n display: block;\n width: 10px;\n height: 10px;\n border: 1px solid var(--primary-color, #1890ff);\n border-radius: 2px;\n }\n\n span[label][selected] {\n background-color: var(--primary-color, #1890ff);\n }\n\n li[checked='checked'] > div[self] > span[checkbox]::before {\n background-color: var(--primary-color, #1890ff);\n border-color: var(--primary-color, #1890ff);\n }\n\n li[checked='checked'] > div[self] > span[checkbox]::after {\n position: absolute;\n content: ' ';\n display: block;\n top: 50%;\n left: 50%;\n width: 3px;\n height: 7px;\n border: 2px solid #fff;\n border-top: none;\n border-left: none;\n -webkit-transform: translate(-50%, -50%) rotate(45deg);\n -ms-transform: translate(-50%, -50%) rotate(45deg);\n transform: translate(-50%, -50%) rotate(45deg);\n }\n\n li[checked='half-checked'] > div[self] > span[checkbox]::before {\n background-color: var(--primary-color, #1890ff);\n border-color: var(--primary-color, #1890ff);\n }\n\n li[checked='half-checked'] > div[self] > span[checkbox]::after {\n position: absolute;\n content: ' ';\n display: block;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 10px;\n height: 2px;\n background-color: #fff;\n }\n `\n\n get rendererTemplate() {\n var { childrenProperty = 'children' } = this.column.record.options || {}\n var { __parent__: parent, __children__: children, __collapsed__: collapsed = true } = this.record\n\n const expandable = childrenProperty in this.record\n\n return html`\n ${expandable\n ? html` <span expander @click=${this.onClickExpander.bind(this)} ?collapsed=${collapsed}></span> `\n : html``}\n <span checkbox></span>\n <span label>${this.value}</span>\n `\n }\n\n updated(changes: PropertyValues<this>) {\n var { __depth__ } = this.record\n\n this.style.setProperty('--tree-depth', String(__depth__))\n }\n\n onClickExpander(e: MouseEvent) {\n e.stopPropagation()\n\n this.record.__collapsed__ = !this.record.__collapsed__\n\n this.requestUpdate()\n }\n\n // onClickItem(e: MouseEvent) {\n // e.stopPropagation()\n\n // const target = e.target as HTMLSpanElement\n // const itemElement = target!.closest('li') as HTMLLIElement\n\n // var item = (itemElement as any).item\n // var checked = itemElement.getAttribute('checked') || ''\n\n // this.walkTreeCheckedUpdate(item, checked == '' || checked == 'unchecked' ? 'checked' : 'unchecked')\n // this.updateCheckedAll(this.data!)\n\n // this.requestUpdate()\n // }\n\n // walkTreeCheckedUpdate(item: TreeItem, checked: string = '') {\n // const { __status__ } = item\n // const children = item[this.childrenProperty] as TreeItem[]\n\n // children?.forEach(child => this.walkTreeCheckedUpdate(child, checked))\n // item.__status__ = {\n // ...__status__,\n // checked\n // }\n // }\n\n // updateCheckedAll(item: TreeItem) {\n // const { __status__ } = item\n // const children = item[this.childrenProperty] as TreeItem[]\n\n // if (!children || children.length == 0) {\n // return\n // }\n\n // children.forEach(child => this.updateCheckedAll(child))\n\n // var checked: string = ''\n\n // children.forEach(child => {\n // const { __status__ = {} } = child\n\n // if (__status__.checked == 'half-checked') {\n // checked = 'half-checked'\n // } else if (__status__.checked == 'checked') {\n // checked = checked == 'checked' || checked == '' ? 'checked' : 'half-checked'\n // } else {\n // checked = checked == 'unchecked' || checked == '' ? 'unchecked' : 'half-checked'\n // }\n // })\n\n // item.__status__ = {\n // ...__status__,\n // checked\n // }\n // }\n}\n"]}
@@ -107,6 +107,8 @@ export type ColumnWidthCallback = (column: ColumnConfig) => string;
107
107
  export type HeaderConfig = {
108
108
  renderer: HeaderRenderer;
109
109
  style?: string;
110
+ group?: string;
111
+ groupStyle?: string;
110
112
  };
111
113
  export type HeaderRenderer = (column: ColumnConfig) => any;
112
114
  export type ValueGeneratorFn = (...args: any[]) => any;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAmFA,MAAM,CAAN,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,mCAAa,CAAA;IACb,yCAAmB,CAAA;IACnB,mCAAa,CAAA;AACf,CAAC,EAJW,kBAAkB,KAAlB,kBAAkB,QAI7B","sourcesContent":["import { TemplateResult } from 'lit-html'\n\nimport { DataCardField } from './data-card/data-card-field'\nimport { DataCardGutter } from './data-card/data-card-gutter'\nimport { RecordCard } from './data-card/record-card'\nimport { DataGridField } from './data-grid/data-grid-field'\nimport { DataListField } from './data-list/data-list-field'\nimport { DataListGutter } from './data-list/data-list-gutter'\nimport { RecordPartial } from './data-list/record-partial'\nimport { DataReportField } from './data-report/data-report-field'\nimport { OxGristEditor } from './editors'\nimport { QueryFilter } from './filters'\nimport { OxGristRenderer } from './renderers/ox-grist-renderer'\n\nexport type GristConfig = {\n columns: ColumnConfig[]\n rows: RowsConfig\n list: ListConfig\n pagination?: PaginationConfig\n sorters?: SortersConfig\n filters?: FiltersConfig\n}\n\nexport type SorterConfig = { name: string; desc?: boolean }\nexport type SortersConfig = SorterConfig[]\nexport type FilterOperator =\n | 'search'\n | 'eq'\n | 'between'\n | 'gte'\n | 'lte'\n | 'is_not_true'\n | 'in'\n | 'like'\n | 'i_like'\n | 'noteq'\n | 'is_empty_num_id'\n | 'is_blank'\n | 'is_present'\n | 'is_not_false'\n | 'is_true'\n | 'is_false'\n | 'is_not_null'\n | 'is_null'\n | 'notin_with_null'\n | 'notin'\n | 'gt'\n | 'lt'\n | 'i_nlike'\n | 'nlike'\n\nexport type FilterConfigObject = {\n type: string\n operator?: FilterOperator\n options?: { [key: string]: any }\n value?: string | number | boolean | string[] | number[] | undefined\n label?: string\n}\nexport type FilterConfig = FilterConfigObject | FilterOperator | boolean\n\nexport type FilterValue = {\n name: string\n operator: FilterOperator\n value: string | number | boolean | string[] | number[] | undefined\n}\n\n/**\n * Configuration options for filters.\n */\nexport type FiltersConfig = {\n /**\n * Specifies whether to provide filtering functionality in the header.\n */\n header?: boolean\n}\n\nexport type PaginationConfig = {\n page?: number\n limit?: number\n pages?: number[]\n infinite?: boolean\n}\n\nexport enum InheritedValueType {\n None = 'None',\n Include = 'Include',\n Only = 'Only'\n}\n\nexport type FetchOption = {\n page?: number\n limit?: number\n sorters?: SortersConfig\n sortings?: SortersConfig\n filters?: QueryFilter[]\n inherited?: InheritedValueType\n options?: object\n}\nexport type FetchResult = {\n page?: number\n limit?: number\n total: number\n records: GristRecord[]\n} | void\nexport type FetchHandler = (param: FetchOption) => Promise<FetchResult>\n\nexport type GristEventHandler = (\n columns: ColumnConfig[],\n data?: GristData,\n column?: ColumnConfig,\n record?: GristRecord,\n rowIndex?: number,\n target?: any\n) => void\n\nexport type AccumulatorFunc =\n | 'sum'\n | 'avg'\n | 'count'\n | 'min'\n | 'max'\n | ((data: GristData, column: ColumnConfig) => string | number)\n\nexport type ColumnConfig = {\n type: string\n name: string\n gutterName?: string\n fixed?: boolean\n header: HeaderConfig\n record: RecordConfig\n handlers: GristEventHandlerSet\n label: LabelConfig\n hidden?: boolean\n sortable?: boolean\n resizable?: boolean\n width?: number | string | ColumnWidthCallback\n forList?: boolean\n validation?: ValidationCallback\n accumulator?: AccumulatorFunc\n filter?: FilterConfig\n imex?: ImexConfig | boolean\n multiple?: boolean\n rowCount?: boolean\n}\n\nexport type ValidationCallback = (after: any, before: any, record: GristRecord, column: ColumnConfig) => boolean\n\nexport type LabelConfig =\n | string\n | boolean\n | {\n renderer: LabelRenderer\n }\n\nexport type LabelRenderer = () => void\n\nexport type ColumnWidthCallback = (column: ColumnConfig) => string\n\nexport type HeaderConfig = {\n renderer: HeaderRenderer\n style?: string\n}\nexport type HeaderRenderer = (column: ColumnConfig) => any\n\nexport type ValueGeneratorFn = (...args: any[]) => any\nexport type DefaultValueFnConfig =\n | {\n /**\n * The name of the default value function to be used for the column.\n */\n name: string\n\n /**\n * The parameters to be passed to the default value function.\n */\n params?: any[]\n }\n | Function\n\nexport type RecordConfig = {\n renderer: FieldRenderer\n editor?: FieldEditor\n editable?: boolean | Function\n mandatory?: boolean\n classifier: GristClassifier\n align?: 'left' | 'right' | 'center'\n options: any\n rowOptionField?: string\n defaultValue?: DefaultValueFnConfig\n [extended: string]: any\n}\n\nexport type FieldRenderer = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n owner:\n | DataGridField\n | RecordCard\n | DataCardGutter\n | DataCardField\n | DataListGutter\n | DataListField\n | RecordPartial\n | DataReportField\n | Element\n) => OxGristRenderer | TemplateResult | string | void\nexport type FieldEditor = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n field: DataGridField\n) => OxGristEditor\n\nexport type FilterSelectRenderer = (\n column: ColumnConfig,\n value: string | number | boolean | string[] | number[] | any | undefined,\n owner: Element\n) => TemplateResult | string | void\n\nexport type GristEventHandlerSet = {\n click?: GristEventHandler\n dblclick?: GristEventHandler\n focus?: GristEventHandler\n}\n\nexport type ListConfig = {\n thumbnail?: string\n fields: string[]\n details: string[]\n}\n\nexport type ImexConfig = {\n header: string\n key: string\n width: number\n type: string\n}\n\nexport type RowsConfig = {\n accumulator?: boolean\n appendable: boolean\n insertable: boolean\n selectable?: RowSelectableConfig\n groups: GroupConfig[]\n totals: string[]\n classifier: GristClassifier\n handlers: GristEventHandlerSet\n}\n\nexport type GristClassifier = (\n record: GristRecord,\n rowIndex: number\n) => { emphasized?: boolean | string | string[]; [key: string]: any } | void\n\nexport type GroupConfig = {\n align: string\n titleColumn?: ColumnConfig\n title: string\n value?: string\n groupName?: string\n row?: number\n column: string | number\n rowspan: number\n colspan?: number\n}\n\nexport type RowSelectableConfig = {\n multiple?: boolean\n}\n\nexport type GristRecord = {\n id?: string\n name?: string\n __seq__?: number\n __dirty__?: string\n __selected__?: boolean\n __changes__?: object[]\n __dirtyfields__?: { [key: string]: any }\n __origin__?: any\n [key: string]: any\n}\n\nexport type GristData = {\n page?: number\n total?: number\n limit?: number\n records: GristRecord[]\n}\n\nexport type GristSelectFunction = (record: GristRecord) => boolean\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAmFA,MAAM,CAAN,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,mCAAa,CAAA;IACb,yCAAmB,CAAA;IACnB,mCAAa,CAAA;AACf,CAAC,EAJW,kBAAkB,KAAlB,kBAAkB,QAI7B","sourcesContent":["import { TemplateResult } from 'lit-html'\n\nimport { DataCardField } from './data-card/data-card-field'\nimport { DataCardGutter } from './data-card/data-card-gutter'\nimport { RecordCard } from './data-card/record-card'\nimport { DataGridField } from './data-grid/data-grid-field'\nimport { DataListField } from './data-list/data-list-field'\nimport { DataListGutter } from './data-list/data-list-gutter'\nimport { RecordPartial } from './data-list/record-partial'\nimport { DataReportField } from './data-report/data-report-field'\nimport { OxGristEditor } from './editors'\nimport { QueryFilter } from './filters'\nimport { OxGristRenderer } from './renderers/ox-grist-renderer'\n\nexport type GristConfig = {\n columns: ColumnConfig[]\n rows: RowsConfig\n list: ListConfig\n pagination?: PaginationConfig\n sorters?: SortersConfig\n filters?: FiltersConfig\n}\n\nexport type SorterConfig = { name: string; desc?: boolean }\nexport type SortersConfig = SorterConfig[]\nexport type FilterOperator =\n | 'search'\n | 'eq'\n | 'between'\n | 'gte'\n | 'lte'\n | 'is_not_true'\n | 'in'\n | 'like'\n | 'i_like'\n | 'noteq'\n | 'is_empty_num_id'\n | 'is_blank'\n | 'is_present'\n | 'is_not_false'\n | 'is_true'\n | 'is_false'\n | 'is_not_null'\n | 'is_null'\n | 'notin_with_null'\n | 'notin'\n | 'gt'\n | 'lt'\n | 'i_nlike'\n | 'nlike'\n\nexport type FilterConfigObject = {\n type: string\n operator?: FilterOperator\n options?: { [key: string]: any }\n value?: string | number | boolean | string[] | number[] | undefined\n label?: string\n}\nexport type FilterConfig = FilterConfigObject | FilterOperator | boolean\n\nexport type FilterValue = {\n name: string\n operator: FilterOperator\n value: string | number | boolean | string[] | number[] | undefined\n}\n\n/**\n * Configuration options for filters.\n */\nexport type FiltersConfig = {\n /**\n * Specifies whether to provide filtering functionality in the header.\n */\n header?: boolean\n}\n\nexport type PaginationConfig = {\n page?: number\n limit?: number\n pages?: number[]\n infinite?: boolean\n}\n\nexport enum InheritedValueType {\n None = 'None',\n Include = 'Include',\n Only = 'Only'\n}\n\nexport type FetchOption = {\n page?: number\n limit?: number\n sorters?: SortersConfig\n sortings?: SortersConfig\n filters?: QueryFilter[]\n inherited?: InheritedValueType\n options?: object\n}\nexport type FetchResult = {\n page?: number\n limit?: number\n total: number\n records: GristRecord[]\n} | void\nexport type FetchHandler = (param: FetchOption) => Promise<FetchResult>\n\nexport type GristEventHandler = (\n columns: ColumnConfig[],\n data?: GristData,\n column?: ColumnConfig,\n record?: GristRecord,\n rowIndex?: number,\n target?: any\n) => void\n\nexport type AccumulatorFunc =\n | 'sum'\n | 'avg'\n | 'count'\n | 'min'\n | 'max'\n | ((data: GristData, column: ColumnConfig) => string | number)\n\nexport type ColumnConfig = {\n type: string\n name: string\n gutterName?: string\n fixed?: boolean\n header: HeaderConfig\n record: RecordConfig\n handlers: GristEventHandlerSet\n label: LabelConfig\n hidden?: boolean\n sortable?: boolean\n resizable?: boolean\n width?: number | string | ColumnWidthCallback\n forList?: boolean\n validation?: ValidationCallback\n accumulator?: AccumulatorFunc\n filter?: FilterConfig\n imex?: ImexConfig | boolean\n multiple?: boolean\n rowCount?: boolean\n}\n\nexport type ValidationCallback = (after: any, before: any, record: GristRecord, column: ColumnConfig) => boolean\n\nexport type LabelConfig =\n | string\n | boolean\n | {\n renderer: LabelRenderer\n }\n\nexport type LabelRenderer = () => void\n\nexport type ColumnWidthCallback = (column: ColumnConfig) => string\n\nexport type HeaderConfig = {\n renderer: HeaderRenderer\n style?: string\n group?: string\n groupStyle?: string\n}\nexport type HeaderRenderer = (column: ColumnConfig) => any\n\nexport type ValueGeneratorFn = (...args: any[]) => any\nexport type DefaultValueFnConfig =\n | {\n /**\n * The name of the default value function to be used for the column.\n */\n name: string\n\n /**\n * The parameters to be passed to the default value function.\n */\n params?: any[]\n }\n | Function\n\nexport type RecordConfig = {\n renderer: FieldRenderer\n editor?: FieldEditor\n editable?: boolean | Function\n mandatory?: boolean\n classifier: GristClassifier\n align?: 'left' | 'right' | 'center'\n options: any\n rowOptionField?: string\n defaultValue?: DefaultValueFnConfig\n [extended: string]: any\n}\n\nexport type FieldRenderer = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n owner:\n | DataGridField\n | RecordCard\n | DataCardGutter\n | DataCardField\n | DataListGutter\n | DataListField\n | RecordPartial\n | DataReportField\n | Element\n) => OxGristRenderer | TemplateResult | string | void\nexport type FieldEditor = (\n value: any,\n column: ColumnConfig,\n record: GristRecord,\n rowIndex: number,\n field: DataGridField\n) => OxGristEditor\n\nexport type FilterSelectRenderer = (\n column: ColumnConfig,\n value: string | number | boolean | string[] | number[] | any | undefined,\n owner: Element\n) => TemplateResult | string | void\n\nexport type GristEventHandlerSet = {\n click?: GristEventHandler\n dblclick?: GristEventHandler\n focus?: GristEventHandler\n}\n\nexport type ListConfig = {\n thumbnail?: string\n fields: string[]\n details: string[]\n}\n\nexport type ImexConfig = {\n header: string\n key: string\n width: number\n type: string\n}\n\nexport type RowsConfig = {\n accumulator?: boolean\n appendable: boolean\n insertable: boolean\n selectable?: RowSelectableConfig\n groups: GroupConfig[]\n totals: string[]\n classifier: GristClassifier\n handlers: GristEventHandlerSet\n}\n\nexport type GristClassifier = (\n record: GristRecord,\n rowIndex: number\n) => { emphasized?: boolean | string | string[]; [key: string]: any } | void\n\nexport type GroupConfig = {\n align: string\n titleColumn?: ColumnConfig\n title: string\n value?: string\n groupName?: string\n row?: number\n column: string | number\n rowspan: number\n colspan?: number\n}\n\nexport type RowSelectableConfig = {\n multiple?: boolean\n}\n\nexport type GristRecord = {\n id?: string\n name?: string\n __seq__?: number\n __dirty__?: string\n __selected__?: boolean\n __changes__?: object[]\n __dirtyfields__?: { [key: string]: any }\n __origin__?: any\n [key: string]: any\n}\n\nexport type GristData = {\n page?: number\n total?: number\n limit?: number\n records: GristRecord[]\n}\n\nexport type GristSelectFunction = (record: GristRecord) => boolean\n"]}
@@ -0,0 +1,26 @@
1
+ import '../src/index.js';
2
+ import '../src/filters/filters-form.js';
3
+ import '../src/sorters/sorters-control.js';
4
+ import '../src/record-view/record-creator.js';
5
+ import '@operato/popup/ox-popup-list.js';
6
+ import '@material/mwc-icon';
7
+ import { TemplateResult } from 'lit';
8
+ declare const _default: {
9
+ title: string;
10
+ component: string;
11
+ argTypes: {
12
+ config: {
13
+ control: string;
14
+ };
15
+ };
16
+ };
17
+ export default _default;
18
+ interface Story<T> {
19
+ (args: T): TemplateResult;
20
+ args?: Partial<T>;
21
+ argTypes?: Record<string, unknown>;
22
+ }
23
+ interface ArgTypes {
24
+ config: object;
25
+ }
26
+ export declare const Regular: Story<ArgTypes>;
@@ -0,0 +1,448 @@
1
+ import '../src/index.js';
2
+ import '../src/filters/filters-form.js';
3
+ import '../src/sorters/sorters-control.js';
4
+ import '../src/record-view/record-creator.js';
5
+ import '@operato/popup/ox-popup-list.js';
6
+ import '@material/mwc-icon';
7
+ import { html } from 'lit';
8
+ const fetchHandler = async ({ page, limit }) => {
9
+ var total = 120993;
10
+ var start = (page - 1) * limit;
11
+ await new Promise(resolve => setTimeout(resolve, 500));
12
+ return {
13
+ total,
14
+ records: Array(limit * page > total ? total % limit : limit)
15
+ .fill('')
16
+ .map((item, idx) => {
17
+ return {
18
+ id: String(idx),
19
+ name: idx % 2 ? `shnam-${start + idx + 1}` : `heartyoh-${start + idx + 1}`,
20
+ description: idx % 2 ? `hatiolabmanager${start + idx + 1}1234567890` : `hatiosea manager-${start + idx + 1}`,
21
+ email: idx % 2 ? `shnam-${start + idx + 1}@gmail.com` : `heartyoh-${start + idx + 1}@gmail.com`,
22
+ active: Math.round(Math.random() * 2) % 2 ? true : false,
23
+ barcode: idx % 2 ? `1234567890${start + idx + 1}` : `0987654321${start + idx + 1}`,
24
+ company: idx % 2
25
+ ? {
26
+ id: '2',
27
+ name: 'HatioLAB',
28
+ description: `경기도 성남시-${start + idx + 1}`
29
+ }
30
+ : {
31
+ id: '3',
32
+ name: 'HatioSEA',
33
+ description: `말레이시아 세티아알람-${start + idx + 1}`
34
+ },
35
+ thumbnail: idx % 4 === 0
36
+ ? '' /* no source */
37
+ : idx % 4 === 1
38
+ ? `http://www.hatiolab.com/assets/img/operato-biz3.png`
39
+ : idx % 4 === 2
40
+ ? `http://www.hatiolab.com/assets/img/thingsboard-30.png`
41
+ : `http://www.hatiolab.com/wrong-url.png` /* wrong source */,
42
+ role: ['admin', 'worker', 'tester'][idx % 3],
43
+ color: idx % 2 ? `#87f018` : `#180f87`,
44
+ rate: Math.round(Math.random() * 100),
45
+ dynamicType: ['text', 'email', 'checkbox', 'color', 'progress', 'barcode'][idx % 5],
46
+ dynamicValue: ['abcdefghijkl', 'heartyoh@hatiolab.com', 'true', 'orange', '50', '1234567890'][idx % 5],
47
+ homepage: idx % 2 ? `http://hatiolab.com/${start + idx + 1}` : `http://deadpool.hatiolab.com/${start + idx + 1}`,
48
+ json5: {
49
+ abc: 'abc',
50
+ value: 123
51
+ },
52
+ createdAt: Date.now(),
53
+ updatedAt: Date.now()
54
+ };
55
+ })
56
+ };
57
+ };
58
+ const config = {
59
+ list: {
60
+ thumbnail: 'thumbnail',
61
+ fields: ['name', 'description'],
62
+ details: ['role', 'email']
63
+ },
64
+ columns: [
65
+ {
66
+ type: 'gutter',
67
+ gutterName: 'dirty',
68
+ fixed: true
69
+ },
70
+ {
71
+ type: 'gutter',
72
+ gutterName: 'sequence',
73
+ fixed: true
74
+ },
75
+ {
76
+ type: 'gutter',
77
+ gutterName: 'row-selector',
78
+ multiple: true,
79
+ fixed: true
80
+ },
81
+ {
82
+ type: 'gutter',
83
+ gutterName: 'button',
84
+ icon: 'edit',
85
+ title: 'edit',
86
+ handlers: {
87
+ click: function () {
88
+ console.log('clicked');
89
+ }
90
+ },
91
+ fixed: true
92
+ },
93
+ {
94
+ type: 'gutter',
95
+ gutterName: 'button',
96
+ icon: 'add',
97
+ title: 'add',
98
+ handlers: {
99
+ click: 'record-copy'
100
+ },
101
+ fixed: true
102
+ },
103
+ {
104
+ type: 'gutter',
105
+ gutterName: 'button',
106
+ icon: 'arrow_downward',
107
+ title: 'download',
108
+ handlers: {
109
+ click: 'move-down'
110
+ },
111
+ fixed: true
112
+ },
113
+ {
114
+ type: 'string',
115
+ name: 'id',
116
+ hidden: true
117
+ },
118
+ {
119
+ type: 'link',
120
+ name: 'name',
121
+ label: true,
122
+ header: 'name',
123
+ record: {
124
+ editable: true,
125
+ options: {
126
+ // href: 'http://hatiolab.com',
127
+ href: function (column, record, rowIndex) {
128
+ return record['homepage'];
129
+ },
130
+ target: '_blank'
131
+ }
132
+ },
133
+ filter: 'search',
134
+ sortable: true,
135
+ width: 120,
136
+ fixed: true
137
+ },
138
+ {
139
+ type: 'string',
140
+ name: 'description',
141
+ header: 'description',
142
+ filter: 'search',
143
+ record: {
144
+ editable: true,
145
+ align: 'left'
146
+ },
147
+ width: 200,
148
+ handlers: {
149
+ click: (columns, data, column, record, rowIndex, target) => {
150
+ alert(`${column.name} ${record[column.name]}, row : ${rowIndex}`);
151
+ }
152
+ }
153
+ },
154
+ {
155
+ type: 'email',
156
+ name: 'email',
157
+ label: true,
158
+ header: 'email',
159
+ record: {
160
+ editable: true
161
+ },
162
+ filter: 'search',
163
+ sortable: true,
164
+ width: 130,
165
+ validation: function (after, before, record, column) {
166
+ if (after.indexOf('@') == -1) {
167
+ document.dispatchEvent(new CustomEvent('notify', {
168
+ detail: {
169
+ type: 'error',
170
+ message: `invalid value - ${after}`
171
+ }
172
+ }));
173
+ return false;
174
+ }
175
+ return true;
176
+ }
177
+ },
178
+ {
179
+ type: 'boolean',
180
+ name: 'active',
181
+ header: 'active',
182
+ record: {
183
+ editable: true
184
+ },
185
+ filter: true,
186
+ handlers: {
187
+ dblclick: () => {
188
+ const grist = document.querySelector('ox-grist');
189
+ console.log(grist.dirtyRecords);
190
+ }
191
+ },
192
+ sortable: true,
193
+ width: 60
194
+ },
195
+ {
196
+ type: 'string[]',
197
+ name: 'role',
198
+ label: true,
199
+ header: 'role',
200
+ record: {
201
+ options: ['', 'admin', 'worker', 'tester'],
202
+ editable: true
203
+ },
204
+ filter: true,
205
+ sortable: true,
206
+ width: 120
207
+ },
208
+ {
209
+ type: 'color',
210
+ name: 'color',
211
+ header: 'color',
212
+ record: {
213
+ editable: true
214
+ },
215
+ sortable: true,
216
+ width: 50
217
+ },
218
+ {
219
+ type: 'float',
220
+ name: 'rate',
221
+ header: 'rate',
222
+ record: {
223
+ align: 'right',
224
+ editable: true,
225
+ defaultValue: 10000.1
226
+ },
227
+ filter: 'between',
228
+ sortable: true,
229
+ width: 50
230
+ },
231
+ {
232
+ type: 'json5',
233
+ name: 'json5',
234
+ header: 'JSON5',
235
+ width: 200
236
+ },
237
+ {
238
+ type: 'image',
239
+ name: 'thumbnail',
240
+ header: 'thumbnail',
241
+ record: {
242
+ editable: true
243
+ },
244
+ width: 120
245
+ },
246
+ {
247
+ type: 'datetime',
248
+ name: 'updatedAt',
249
+ header: 'updated at',
250
+ record: {
251
+ editable: true,
252
+ defaultValue: {
253
+ name: 'now'
254
+ }
255
+ },
256
+ filter: 'between',
257
+ sortable: true,
258
+ width: 180
259
+ },
260
+ {
261
+ type: 'datetime',
262
+ name: 'createdAt',
263
+ header: 'created at',
264
+ record: {
265
+ editable: false
266
+ },
267
+ sortable: true,
268
+ width: 180
269
+ }
270
+ ],
271
+ rows: {
272
+ selectable: {
273
+ multiple: true
274
+ },
275
+ handlers: {
276
+ focus: 'select-row-toggle'
277
+ },
278
+ classifier: function (record, rowIndex) {
279
+ const rate = record['rate'];
280
+ const emphasized = rate < 10 ? ['black', 'white'] : rate < 25 ? ['yellow', 'blue'] : rate < 40 ? ['cyan', 'red'] : undefined;
281
+ return {
282
+ emphasized
283
+ };
284
+ }
285
+ },
286
+ sorters: [
287
+ {
288
+ name: 'name',
289
+ desc: true
290
+ },
291
+ {
292
+ name: 'email'
293
+ }
294
+ ],
295
+ pagination: {
296
+ pages: [20, 30, 50, 100, 200]
297
+ }
298
+ };
299
+ export default {
300
+ title: 'fixed column',
301
+ component: 'ox-grist',
302
+ argTypes: {
303
+ config: { control: 'object' }
304
+ }
305
+ };
306
+ const Template = ({ config }) => html ` <link
307
+ href="https://fonts.googleapis.com/css?family=Material+Icons&display=block"
308
+ rel="stylesheet"
309
+ />
310
+ <link href="/themes/app-theme.css" rel="stylesheet" />
311
+ <link href="/themes/oops-theme.css" rel="stylesheet" />
312
+ <link href="/themes/grist-theme.css" rel="stylesheet" />
313
+
314
+ <style>
315
+ [slot='headroom'] {
316
+ display: flex;
317
+ flex-direction: row;
318
+ align-items: center;
319
+ padding: var(--padding-default) var(--padding-wide);
320
+ background-color: var(--theme-white-color);
321
+ box-shadow: var(--box-shadow);
322
+
323
+ --mdc-icon-size: 24px;
324
+ }
325
+ #sorters mwc-icon,
326
+ #modes mwc-icon {
327
+ --mdc-icon-size: 18px;
328
+ }
329
+ #sorters {
330
+ margin-left: auto;
331
+ margin-right: var(--margin-default);
332
+ padding-left: var(--padding-narrow);
333
+ border-bottom: var(--border-dark-color);
334
+ position: relative;
335
+ color: var(--secondary-color);
336
+ font-size: var(--fontsize-default);
337
+ user-select: none;
338
+ }
339
+
340
+ #sorters > * {
341
+ padding: var(--padding-narrow);
342
+ vertical-align: middle;
343
+ }
344
+
345
+ #modes > * {
346
+ padding: var(--padding-narrow);
347
+ opacity: 0.5;
348
+ color: var(--primary-text-color);
349
+ cursor: pointer;
350
+ }
351
+
352
+ #modes > mwc-icon[active] {
353
+ border-radius: 9px;
354
+ background-color: rgba(var(--primary-color-rgb), 0.05);
355
+ opacity: 1;
356
+ color: var(--secondary-text-color);
357
+ cursor: default;
358
+ }
359
+
360
+ #modes > mwc-icon:hover {
361
+ opacity: 1;
362
+ color: var(--secondary-text-color);
363
+ }
364
+
365
+ #add {
366
+ width: 50px;
367
+ text-align: right;
368
+ }
369
+
370
+ #add button {
371
+ background-color: var(--primary-color);
372
+ border: 0;
373
+ border-radius: 50%;
374
+ padding: 5px;
375
+ width: 36px;
376
+ height: 36px;
377
+ cursor: pointer;
378
+ }
379
+
380
+ #add button:hover {
381
+ background-color: var(--focus-background-color);
382
+ box-shadow: var(--box-shadow);
383
+ }
384
+
385
+ #add button mwc-icon {
386
+ font-size: 2em;
387
+ color: var(--theme-white-color);
388
+ }
389
+
390
+ #filters {
391
+ display: flex;
392
+ justify-content: center;
393
+ align-items: center;
394
+ }
395
+
396
+ #filters * {
397
+ margin-right: var(--margin-default);
398
+ }
399
+
400
+ @media only screen and (max-width: 460px) {
401
+ #filters {
402
+ flex-direction: column;
403
+ }
404
+
405
+ #modes {
406
+ display: none;
407
+ }
408
+ }
409
+ </style>
410
+
411
+ <ox-grist
412
+ mode="GRID"
413
+ .config=${config}
414
+ .fetchHandler=${fetchHandler}
415
+ @filters-change=${(e) => console.log('filters', e.target.filters)}
416
+ >
417
+ <div slot="headroom">
418
+ <div id="filters">
419
+ <ox-filters-form autofocus></ox-filters-form>
420
+ </div>
421
+
422
+ <div id="sorters">
423
+ Sort
424
+ <mwc-icon
425
+ @click=${(e) => {
426
+ const target = e.currentTarget;
427
+ target.closest('#sorters').querySelector('#sorter-control').open({
428
+ right: 0,
429
+ top: target.offsetTop + target.offsetHeight
430
+ });
431
+ }}
432
+ >expand_more</mwc-icon
433
+ >
434
+ <ox-popup id="sorter-control">
435
+ <ox-sorters-control> </ox-sorters-control>
436
+ </ox-popup>
437
+ </div>
438
+
439
+ <ox-record-creator id="add" light-popup>
440
+ <button><mwc-icon>add</mwc-icon></button>
441
+ </ox-record-creator>
442
+ </div>
443
+ </ox-grist>`;
444
+ export const Regular = Template.bind({});
445
+ Regular.args = {
446
+ config
447
+ };
448
+ //# sourceMappingURL=fixed-column.stories%20copy.js.map