Compare commits

...

210 Commits

Author SHA1 Message Date
Ejaaz Khan
487908974f
Merge pull request #207 from iamejaaz/sticky-column-fix
fix: total row and scrolling issue
2025-03-07 17:24:23 +05:30
Ejaaz Khan
bf65f4bc9f fix: total row and scrolling issue 2025-03-07 17:17:50 +05:30
Ejaaz Khan
82582a0b20
Merge pull request #205 from iamejaaz/sticky-column
feat: add support for sticky columns
2025-02-21 14:49:31 +05:30
Ejaaz Khan
007a7bf92a refactor: add flex to header to avoid overlow issue 2025-02-21 14:42:36 +05:30
Ejaaz Khan
a5cc12f7ec fix: alignment issue without checkbox column 2025-02-20 15:30:29 +05:30
Ejaaz Khan
4758cf2cd4 test: add dynamic width in resize column 2025-02-19 19:13:15 +05:30
Ejaaz Khan
584d964a72 test: fix resize column test 2025-02-19 17:48:34 +05:30
Ejaaz Khan
6d381d8f6b test: fix failing tests 2025-02-19 17:40:17 +05:30
Ejaaz Khan
f1c2cd916c feat: add support for sticky columns 2025-02-19 16:04:59 +05:30
Soham Kulkarni
f05049c67a
fix: add abiltiy to copy total row (#204) 2025-02-06 19:45:03 +05:30
Smit Vora
ba9814228d
fix: reset the footer to ensure old totals are cleared (#202) 2025-02-06 14:43:14 +05:30
Faris Ansari
4b124f0b1d
chore: simplify usage section 2024-12-12 20:36:13 +05:30
Faris Ansari
9193fd9b06
Update README.md (#201) 2024-12-12 16:14:54 +05:30
Ankush Menat
ecc660ed68
fix: inline filter with html chars (#198)
Filtering with `&` doesn't work because it gets escaped in HTML.
2024-04-17 19:02:02 +05:30
Ankush Menat
bed2708bd5
perf: rendering large data (#197)
visibleRowIndices.includes is major culprit in rendering data table.
This is because for every row it does this computation, so instead of
O(N) operation it becomes O(N^2)
2024-03-13 17:40:57 +05:30
Shariq Ansari
dbde62ce40
Merge pull request #194 from RitvikSardana/develop-ritvik-rowWidth-fix
fix: row width fix to remove unnecessary scroll bar
2024-01-08 15:14:16 +05:30
RitvikSardana
fb465579a0 fix: row width fix to remove unnecessary scroll bar 2024-01-08 15:04:58 +05:30
Shariq Ansari
cca14e09eb
Merge pull request #192 from shariquerik/minor-fix-4
fix: store all data and data in separately
2024-01-03 14:55:10 +05:30
Shariq Ansari
dc09e1d1b2 fix: store all data and data in separately 2024-01-03 14:53:36 +05:30
Shariq Ansari
007ec69609
Merge pull request #191 from shariquerik/minor-fix-3
fix: minor fix
2024-01-03 14:29:05 +05:30
Shariq Ansari
e209ec2ed0 fix: minor fix 2024-01-03 14:27:19 +05:30
Shariq Ansari
89926b680c
Merge pull request #190 from shariquerik/minor-fix-2
fix: minor fix
2024-01-03 14:21:58 +05:30
Shariq Ansari
bbb4eb1f7e
Merge branch 'master' into minor-fix-2 2024-01-03 14:21:49 +05:30
Shariq Ansari
b0e6261285 fix: minor fix 2024-01-03 14:20:01 +05:30
Shariq Ansari
89d3149695
Merge pull request #189 from shariquerik/chore-1
chore: condition fix
2024-01-03 14:13:23 +05:30
Shariq Ansari
497d420ef6 chore: condition fix 2024-01-03 14:12:27 +05:30
Shariq Ansari
f46e7142b7
Merge pull request #188 from shariquerik/load-data
fix: load cell data from data list
2024-01-03 14:06:30 +05:30
Shariq Ansari
04a0202fc3 fix: load cell data from data list 2024-01-03 14:05:49 +05:30
Saqib Ansari
dfbd98a502
chore: fix linter 2024-01-02 12:24:06 +05:30
Saqib Ansari
ae07ce3712
Merge pull request #187 from frappe/nextchamp-saqib-patch-1
fix: `data` is undefined
2024-01-02 12:20:52 +05:30
Saqib Ansari
b3ff486934
fix: data is undefined 2024-01-02 12:17:50 +05:30
Shariq Ansari
98835d8d59 chore: linter fix 2023-12-21 14:16:28 +05:30
Shariq Ansari
1979641623
Merge pull request #186 from shariquerik/send-data-to-formatter2
fix: getdata from datamanager if data is datamanager object
2023-12-21 14:08:38 +05:30
Shariq Ansari
b8a1af27d2 fix: getdata from datamanager if data is datamanager object 2023-12-21 14:08:12 +05:30
Shariq Ansari
f0044603a1
Merge pull request #185 from shariquerik/send-data-to-formatter1
fix: send data of that cell to formatter
2023-12-21 13:39:16 +05:30
Shariq Ansari
49cc051392 fix: send data of that cell to formatter 2023-12-21 13:38:31 +05:30
Shariq Ansari
d29615bca9
Merge pull request #184 from shariquerik/send-data-to-formatter
fix: send data to formatter
2023-12-21 13:28:43 +05:30
Shariq Ansari
48d5b97e55 fix: send data to formatter 2023-12-21 13:27:43 +05:30
Shariq Ansari
8f074a80c2
Merge pull request #183 from shariquerik/filter-row-pass-data
fix: pass data as second parameter to filterMethod
2023-12-21 13:20:14 +05:30
Shariq Ansari
2c36123663 fix: pass data as second parameter to filterMethod 2023-12-21 13:19:34 +05:30
Shariq Ansari
50b44bfb09
fix: added dataManager in filterRows function 2023-12-21 13:13:41 +05:30
Shariq Ansari
6d29880fb7
Merge pull request #182 from shariquerik/pass-filter-instead-of-true
fix: pass filter instead of true
2023-12-07 19:18:37 +05:30
Shariq Ansari
739182562a fix: pass filter instead of true 2023-12-07 19:14:10 +05:30
Faris Ansari
b21e547794
fix: appendRows bug (#181)
Co-authored-by: ArunMuthuram <65416680+ArunMuthuram@users.noreply.github.com>
2023-11-21 13:24:35 +05:30
Suraj Shetty
7ee8ca9304
fix: 🐛 Apply range filter only for numbers (#176) 2023-09-11 13:05:09 +05:30
Matheo D
17cddc05f1
chore: remove .DS_STORE file (#165) 2023-06-17 21:21:19 +05:30
Shariq Ansari
ddb2dd3986
Merge pull request #169 from frappe/filter-with-content-value 2023-03-03 11:43:15 +05:30
Shariq Ansari
71272f8f9e
fix: get content value for specific fieldtypes 2023-03-03 11:33:03 +05:30
Shariq Ansari
6c79a19b29
fix: escape html to get tooltip text (#167) 2023-01-25 18:37:24 +05:30
Shariq Ansari
f990c1ff87
Merge pull request #161 from JulioJair/feature/rowmanager/check-all-filtered 2023-01-25 17:13:03 +05:30
Shariq Ansari
4f07f5fe4e
Merge pull request #166 from shariquerik/dt-cell-tooltip 2023-01-25 17:09:42 +05:30
Shariq Ansari
0b1ce23a33 fix: show datatable cell content in tooltip on hover 2023-01-25 16:53:34 +05:30
Julio de Alba
aa3f3be67f feat: 🎸 check all filtered rows
When datatable has filters applied, and the "check all" is used, it will
check only the rows that match the filters.
2022-09-22 07:07:20 -05:00
Shariq Ansari
eda1540f3b
Merge pull request #158 from phot0n/fix-filter-custom-formatter 2022-06-10 14:28:22 +05:30
phot0n
430d7cfe48 fix: use custom formatter when filtering rows
As datatable renders only some rows at a time and sets each
cell's html property based on that rendering, so when someone
filters things and only looks at some rows without scrolling the whole
grid and tries to filter things (when there is a custom cell formatter),
they won't be able to see the data as the filtering is done based on cell's
content not html (as it's not set at that point)
2022-06-10 11:51:11 +05:30
Shariq Ansari
b4428639cb
Merge pull request #157 from shariquerik/keyboard-integration-fix 2022-06-08 19:57:17 +05:30
Shariq Ansari
d6a6c16fdf chore: minor fix 2022-06-08 19:51:19 +05:30
Shariq Ansari
42a236d9c8 fix: use left, right, up, down keys while editing cell value 2022-06-08 19:11:57 +05:30
Shariq Ansari
d2c4e21cca fix: Dont save if value is same 2022-06-08 19:10:41 +05:30
Shariq Ansari
fea45f4a31
Merge pull request #156 from frappe/fix-link 2022-06-03 15:36:10 +05:30
Hussain Nagaria
59e7f6635a fix: Homepage Link 2022-06-03 15:07:57 +05:30
Shariq Ansari
a278075247
Merge pull request #143 from shariquerik/add-tooltip-on-filter 2022-01-29 11:27:32 +05:30
Shariq Ansari
5db24187e6
Merge branch 'master' into add-tooltip-on-filter 2022-01-27 12:51:06 +05:30
Faris Ansari
95a239963c fix: column resizing in rtl direction 2022-01-17 16:38:49 +05:30
Faris Ansari
4549a5176b
chore: update cypress, github actions and fix tests (#148) 2022-01-05 17:57:02 +05:30
Faris Ansari
6fcce176c3 chore: remove rule for comma-dangle 2022-01-04 15:03:36 +05:30
Faris Ansari
00d924c5c0 chore: update ubuntu version 2022-01-04 12:57:29 +05:30
Himanshu
6d6602f202
feat: translations (#145)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Faris Ansari <netchamp.faris@gmail.com>
2022-01-03 18:41:52 +05:30
Shariq Ansari
88a361f053 fix: Adding tooltip on filter inputs 2021-09-28 13:43:59 +05:30
Shariq Ansari
8ceadaccba
fix: update rowIndex width when row count changes (#138)
* fix: update rowIndex width when row count changes

* fix: removed unwanted code

* fix: Added Refresh Data button
2021-08-20 17:07:35 +05:30
Faris Ansari
cb2729424b chore: Add github action badge 2020-09-16 23:14:49 +05:30
Faris Ansari
0042e3172b chore: Add github token env 2020-09-16 23:06:57 +05:30
Faris Ansari
16983cfbf0
chore: Github Actions configuration (#113) 2020-09-16 22:59:51 +05:30
Prssanna Desai
279a876f7d
fix: 🐛 scroll for rtl direction (#111) 2020-09-16 11:45:20 +05:30
Christopher G. Purbaugh
a0901768d7
Minor grammatical change (#45) 2020-04-01 11:11:54 +05:30
mergify[bot]
c263ab410e
Merge pull request #98 from prssanna/sort-fix
fix: 🐛 set content of empty cell to empty string for sorting
2020-03-16 09:35:23 +00:00
prssanna
18964c0346 fix: 🐛 set content of empty cell to empty string for sorting 2020-02-29 15:56:36 +05:30
Faris Ansari
3475f6ccaa fix: Set cell height explicitly 2019-12-09 22:35:53 +05:30
Faris Ansari
31264d1f4d feat: Ability to disable column reordering 2019-10-01 00:12:17 +05:30
Faris Ansari
1a9c11edf0 fix: Render fluid layout if first row doesnt exist 2019-09-28 14:00:18 +05:30
Faris Ansari
1be233d054 chore: Install libgconf for travis run 2019-09-27 02:45:33 +05:30
Faris Ansari
d92fc5e969 feat: Allow overriding individual components 2019-09-17 19:00:11 +05:30
Faris Ansari
ad0e0b592a fix: Render blank state when rows is 0 2019-09-17 18:59:14 +05:30
Faris Ansari
a47e1f07eb fix: Move past non focusable cell while navigating 2019-08-11 18:00:39 +05:30
Faris Ansari
8b9cd641b1
chore: Update DataTable Logo 2019-07-06 22:59:12 +05:30
Deepesh Garg
3712aaada3 fix: Navigation fix using tabs in inline filters (#75) 2019-07-05 13:14:55 +05:30
mergify[bot]
63bfb5658d
Merge pull request #74 from frappe/fix-typo
chore: Typo
2019-06-29 07:05:59 +00:00
Faris Ansari
b565f32886
chore: Typo 2019-06-28 15:39:56 +05:30
Faris Ansari
df5e4af0ee
chore: Add mergify config (#73) 2019-06-28 15:38:48 +05:30
Faris Ansari
cccb4aa523
Merge pull request #72 from frappe/number-filter-cell-submit-always
Number compare and Submit cell
2019-06-24 15:35:49 +05:30
Faris Ansari
40c8e51e86 fix: Set dropdown background as header background 2019-06-24 15:19:09 +05:30
Faris Ansari
b1962dffcf fix: Submit cell if editing deactivated
Editing cell will now submit the value when the editing is deactivated,
except when Escape key is pressed.
It will also submit when Tab key is pressed.
2019-06-24 15:11:24 +05:30
Faris Ansari
1a0eba65a0 fix: Number compare
- 006 will match abc006
- 1230 will match 1,230 (formatted), 1230 and abc1230
2019-06-24 15:08:39 +05:30
Deepesh Garg
bbca6d8f60 fix: String compare if hay is alphanumeric 2019-06-11 15:27:22 +05:30
Deepesh Garg
e25ef86a63 fix: Parse keyword as float (#68) 2019-05-28 17:45:53 +05:30
Deepesh Garg
b075fb5d71 fix: Compare number using contains instead of equals (#67) 2019-05-23 12:51:18 +05:30
Deepesh Garg
e014960e97
feat: Add support for RTL (#64) 2019-04-26 13:10:10 +05:30
Saif
67f9717827 perf: ️ Expand All and Collapse All render only once (#63)
* perf: ️ Expand All and Collapse All render only once

* Introduce setTreeDepth() function
2019-04-25 12:37:56 +05:30
Faris Ansari
17295a1209 test: Fix column resize test 2019-04-24 11:43:33 +05:30
Faris Ansari
8d96f953cd fix: Allow column to be resized below naturalWidth
- Also honor minimumColumnWidth option
2019-04-24 11:22:33 +05:30
Faris Ansari
ffab69de34 test: Fix test Inline Filters 2019-04-16 13:27:08 +05:30
Faris Ansari
944010d113 fix: Filter numeric columns by value
No need to use = to filter numeric values by their value
2019-04-16 12:43:21 +05:30
Faris Ansari
007579f55e feat: Add API for expand/collapse tree nodes 2019-04-05 17:02:31 +05:30
Faris Ansari
637d37deb8 feat: Add not equals (!=) filter 2019-04-03 23:10:00 +05:30
Faris Ansari
9a5a0b66e8 test: Fix column resize test 2019-03-30 18:25:58 +05:30
Faris Ansari
3b088a9d78 test: Add resize column tests 2019-03-30 18:13:00 +05:30
Faris Ansari
f191c8bd53 tests: Split tests into files 2019-03-30 17:47:43 +05:30
Faris Ansari
7b325a5ba0 feat: Double click resize handle to resize to perfect width 2019-03-30 17:22:25 +05:30
Faris Ansari
3446488b08 fix: Allow column resize past minWidth upto 30px 2019-03-30 17:21:08 +05:30
Faris Ansari
1845adc46e fix: Scroll problem when cell is focused 2019-03-30 17:19:23 +05:30
Saif
0f44a51da3 feat: 🎸 Total Accumulator hook for totals row (#55)
* feat: 🎸 Accumulator event for totals row

* fix: 🐛 Changed values passed to accumulator function

and moved function to hooks.totalAccumulator

* fix: 🐛 Total row set null for column without any number

* fix: Refactor accumulator

- Rename to columnTotal

* fix: 🐛 Prevent showing 0 in total row for Data fieldtype

* fix: Remove hooks object from parent

* fix: Remove separate hooks initialization
2019-02-12 19:51:13 +05:30
Faris Ansari
836b13bd88 test: 💍 Fix test for header dropdown 2019-01-14 14:02:05 +05:30
Faris Ansari
3e044a5ffa feat: 🎸 Add compareValue hook to control comparison
For custom comparison where the formatted value is different than the
actual value, users will mostly compare using the formatted string. Use
compareValue to return parsed cell value and keyword.
2019-01-14 13:34:04 +05:30
Faris Ansari
3fc8ac9e72 fix: 🐛 Dropdown z-index problem
Z-index of dropdown does not work because it's parent has a transform
property. To avoid this, we float the dropdown using fixed co-ordinates
and then update the x, y positions.
2018-12-28 19:16:26 +05:30
Faris Ansari
d494590b70 test: 💍 Add test for string comparison filter 2018-12-28 18:10:54 +05:30
Faris Ansari
acfe0e49cd fix: 🐛 Comparison now also works with strings
Inline filters like >2011/04/01 will work as expected. Works with >, <
and :
2018-12-28 18:10:24 +05:30
Faris Ansari
26a37b539f
Merge pull request #52 from SaiFi0102/fixes
Minor Scrolling Fixes
2018-12-22 13:23:29 +05:30
Faris Ansari
302c1f83d5 fix: 🐛 Smarter setting of bodyHeight based on overflow
When there are only a couple of rows, the scrollbar causes a scroll
which is not needed. We check if the overflow amount is less than the
size of scrollbar and then forcefully disable the scrolling.
2018-12-22 13:02:31 +05:30
Saif Ur Rehman
d6aa52fb16 fix: 🐛 scrolling and height fixes 2018-12-20 05:33:39 +05:00
Faris Ansari
92dcc94b62 test: 💍 Update row selector 2018-12-13 18:02:55 +05:30
Faris Ansari
19fb921ed2 fix: 🐛 Border styling for header 2018-12-13 18:01:30 +05:30
Faris Ansari
bdbdc90578 fix: 🐛 Initialize hyperlist with width/height of container 2018-12-13 18:01:01 +05:30
Faris Ansari
d14a16c9a0 chore: 🤖 Remove old docs folder 2018-12-13 17:44:53 +05:30
Faris Ansari
fb4370020f fix: 🐛 Always show vertical scrollbar
Decouple bodyScrollable from header. Now header position is synced based
on scrollLeft of bodyScrollable. Also, remove redundant .dt-body
container.
2018-12-13 16:59:30 +05:30
Faris Ansari
1791b30077 test: 💍 Add test for the equals keyword 2018-12-10 20:10:07 +05:30
Faris Ansari
b6d4a0eea7 feat: 🎸 Add = keyword for equality matching
Support = keyword for equality matching in numeric columns
2018-12-10 20:05:19 +05:30
Faris Ansari
188b61bf1b test: 💍 Add test for filters with sorting 2018-12-10 19:52:53 +05:30
Faris Ansari
c559faa67b fix: 🐛 Sort filtered results
Fixes the problem where when you sort rows and then try to filter, the
sort order is lost.

closes #51
2018-12-10 19:30:12 +05:30
Faris Ansari
1e20502d09 fix: 🐛 Extract getTotalRow into a function 2018-12-07 17:15:08 +05:30
Faris Ansari
96c7b723da fix: 🐛 Fallback to cell content when html is not available 2018-12-07 15:23:22 +05:30
Faris Ansari
164979f7d6
Merge pull request #50 from frappe/total-row
feat: 🎸 Total Row
2018-11-29 17:04:56 +05:30
Faris Ansari
f5df997c00 feat: 🎸 Total Row
A persistent row which shows the total of the columns which have numeric
values. It also updates when the rows are filtered.
2018-11-29 16:39:35 +05:30
Faris Ansari
f791c91df6 fix: 🐛 Numeric comparison should compare with cell value
Numeric comparison like >, < should compare with the cell value and not
the formatted html value
2018-11-29 13:41:07 +05:30
Faris Ansari
ec08d75261 fix: 🐛 Filter rows based on the formatted value
Earlier filtering was done based on the original value of the cell, but
formatters could change the display value
2018-11-29 13:19:56 +05:30
Faris Ansari
33f7ab9c25 fix: 🐛 Reset body height before setting the bodyStyle
In case when there are very less number of rows, the height will adapt
to a lower value. But in the next refresh the number of rows could
increase so the body height should be reset to it's original value
2018-11-28 00:15:31 +05:30
Faris Ansari
89f13b530a fix: 🐛 Skip setting cellHeight in setDimensions
cellHeight (row height technically) is now handled by hyperlist
directly, so no need to do it manually
2018-11-20 13:34:04 +01:00
Faris Ansari
6fe075467d fix: 🐛 Sort rows numerically
Default sort method uses alphabetical sort
2018-11-01 16:40:35 +05:30
Faris Ansari
cfbb891737 fix: 🐛 Adapt container height when there are less rows 2018-11-01 15:26:11 +05:30
Faris Ansari
cc27d66138
chore: Update Readme 2018-11-01 11:38:40 +05:30
Faris Ansari
1f27d194ca fix: 🐛 Remove non-existing bindEvents method call 2018-11-01 11:29:15 +05:30
Faris Ansari
7eaabcbfe0 fix: 🐛 Reimplement tree node toggle logic
Earlier, rows were hidden using style. Now, since we are using
hyperlist, it is better to refresh the whole list.
2018-10-31 19:09:40 +05:30
Faris Ansari
7f779a3dd8 test: 💍 Add test for multiple filters 2018-10-16 14:16:22 +05:30
Faris Ansari
932afb6cbe feat: 🎸 Support for multiple filters in columns
Multiple inline filters will be applied as AND filters
2018-10-16 13:56:10 +05:30
Faris Ansari
ffe0d2a3f5 style: 💄 Fix ESLint 2018-10-11 13:14:33 +05:30
Faris Ansari
84b7fa3d83 fix: 🐛 More robust column width calculation
Set minimum column width based on options (default 70), also measure
rowIndex column directly in DOM
2018-10-11 13:05:25 +05:30
Faris Ansari
01d74ef6ad fix: 🐛 Pick cellHeight from options
HyperList should take cellHeight from options
2018-10-10 16:49:44 +05:30
Faris Ansari
cbe9f4858b
Merge pull request #49 from frappe/hyperlist
feat: 🎸 Use HyperList instead of Clusterize to render rows
2018-10-10 14:40:02 +05:30
Faris Ansari
51b112687e feat: 🎸 Use HyperList instead of Clusterize to render rows
Clusterize failed to load rows if they were above 1000, this may be a
problem of datatable itself, since it has a complex DOM structure, but
hyperlist works better in it's case.
2018-10-10 14:34:56 +05:30
Faris Ansari
f790a6729a fix: 🐛 Show all rows in restore state
When you refresh datatable after some rows are hidden, after refresh
they remain hidden.
2018-10-05 15:22:47 +05:30
Faris Ansari
d01e045157 Merge branch 'master' of https://github.com/frappe/datatable 2018-09-23 14:54:25 +05:30
Faris Ansari
2ce132c0bc fix(class selector): Replace all selectors with class based selectors
Class based selectors are faster than attribute based ones.
2018-09-23 14:54:00 +05:30
Faris Ansari
4f57bb1b64 fix(filterRows): Show/hide rows using setStyle and removeStyle
Finding row elements and adding/removing classes on them is slower and unreliable
2018-09-23 14:45:12 +05:30
Faris Ansari
87a44a94f0
chore: Add blog link to readme 2018-08-23 21:13:48 +05:30
Faris Ansari
a04a5a1d7c fix(columnWidth): Compensate for scrollbar only if there is vertical scroll 2018-08-23 14:22:35 +05:30
Faris Ansari
7531e13d17 feat(icons): Use feather icons instead of raw ascii strings 2018-08-23 13:52:58 +05:30
Faris Ansari
307de6ed55 test(filter): Add test for the new filter feature 2018-08-09 14:20:55 +05:30
Faris Ansari
cdb276abfd feat(filter): Filter now supports complex queries for Number columns
More advanced filter support for Number columns

For e.g
  >5000 filters rows with the cell value greater than 5000
  <30 filters rows with cell value less than 30
  20:340 filters rows with cell value in the range of 20 and 340

You can also provide a custom filter function by providing a
`filterRows` function in the options during initialization.
2018-08-09 13:30:02 +05:30
Faris Ansari
ae6415c950 fix(cell): Disable text selection to enable drag to select in cells 2018-07-20 10:48:21 +05:30
Faris Ansari
bbc54853c8 fix(build): Fix typo to generate production assets 2018-07-19 18:18:12 +05:30
Faris Ansari
ffac2cbe0b ci(travis): Run semantic release directly 2018-07-19 15:39:03 +05:30
Faris Ansari
18fa7d2eca fix(style): Make cell take full height in firefox 2018-07-19 14:59:44 +05:30
Faris Ansari
7417b0fdcd fix(style): Namespace CSS variables to avoid clash with foreign CSS variables 2018-07-19 14:42:30 +05:30
Faris Ansari
1f387c9079 fix(theme): Include CSS variables in the final css build
CSS Variables should be built into the distributable css file for theme support
2018-07-19 14:28:03 +05:30
Faris Ansari
312335d419 chore(cdn): Add filepath in package.json for CDNs
Add the filepath for CDNs unpkg and jsdelivr so that they serve minified files by default
2018-07-14 11:43:25 +05:30
Faris Ansari
0856f6cdf7 fix(css): Table cell border fix for firefox and edge browser
Firefox and Edge paints the cell background over the border of the cell. This fixes that.
2018-07-14 11:32:16 +05:30
Faris Ansari
226aea24df fix(toggleFilter): Return early if inlineFilter is disabled 2018-07-14 11:31:45 +05:30
Faris Ansari
8db22e9db2
Merge pull request #39 from frappe/npm-bundle-size-badge
Add npm bundle size badge
2018-07-12 23:30:41 +05:30
Faris Ansari
cc6128b2b6
Add npm bundle size badge 2018-07-12 23:29:20 +05:30
Faris Ansari
bddb3c27ce fix(destroy): Cleanup event listeners on destroy
Event listeners attached to elements inside the root datatable wrapper are automatically removed
when the root element is removed from DOM. But, there are event listeners which are attached to
window and body, those have to be manually removed. This commit introduces an internal event system,
through which we destroy global event handlers.
2018-07-10 12:00:41 +05:30
Faris Ansari
f9714673b4 fix(rowmanager): Fire onCheckRow event on checkAll
When checkAll is triggered via API or UI input, onCheckRow event fires

Closes #38
2018-07-09 17:35:59 +05:30
Faris Ansari
78666f8ab2 fix(theme): More CSS variables for themes
Add CSS variables for header cell background and toast message border for more flexible themeing
2018-07-08 23:17:11 +05:30
Faris Ansari
1b5292a7f0 feat(theme): Add dark theme
Add a new stylesheet which overrides the css variables in the core stylesheet to obtain a dark theme
2018-07-08 19:13:21 +05:30
Faris Ansari
2cf2191f0f fix(treeView): Decrease left padding of tree node cells 2018-07-08 17:35:16 +05:30
Faris Ansari
9e4d0bb9c8 chore(lint): Add lint step in travis
Add lint step in travis, add lint and commit script in package.json. Update Contributing information
in Readme
2018-07-07 23:49:45 +05:30
Faris Ansari
5f25514639 chore(readme): Add semantic release badge 2018-07-07 23:29:37 +05:30
Faris Ansari
9ed446194d chore(release): Build files before running tests
Files should be built before running tests, since Cypress requires those files
2018-07-07 22:30:15 +05:30
Faris Ansari
48e0034eb1 test: Remove default sort and let test pass 2018-07-07 22:28:29 +05:30
Faris Ansari
47aa83dce8 chore(release): Add semantic release and commitizen 2018-07-07 22:10:15 +05:30
Faris Ansari
d225308151 feat(sort): Apply sortOrder in initialization 2018-07-07 22:07:16 +05:30
Faris Ansari
cab9fff5e6 v0.0.10 2018-07-05 13:25:15 +05:30
Faris Ansari
3a050cdfb6 [fix] Cells with 0 value should be right aligned 2018-07-05 13:23:56 +05:30
Faris Ansari
cd4645e5b3
Cache npm and cache directories, and run test using npm (#34) 2018-06-06 23:19:16 +05:30
Faris Ansari
f040f5c584 v0.0.9 2018-06-05 16:09:01 +05:30
Faris Ansari
978682e1df
getEditor (#33)
* chore

- Add rollup devDependency
- Add tsconfig for cypress intellisense

* getEditor: getValue can now return a Promise that resolves to the value
2018-06-05 15:58:39 +05:30
Faris Ansari
2af2cebf14 Update cypress to 3.0.1 2018-06-03 17:08:46 +05:30
Faris Ansari
2130a46c21 [tests] Edit cell 2018-06-03 17:08:36 +05:30
Faris Ansari
96a9235a12
Update README.md (#32) 2018-05-31 00:16:16 +05:30
Faris Ansari
c95fa505dd
Update npm version badge (#31)
* Update npm version badge

* Update license badge
2018-05-30 15:02:04 +05:30
Faris Ansari
bc3bd3accb
Merge pull request #30 from frappe/shields
Add badges
2018-05-30 08:35:44 +05:30
Faris Ansari
291cc101f5 Badges in center 2018-05-30 08:33:33 +05:30
Faris Ansari
fe79d6b2ec Update badge links 2018-05-30 08:31:01 +05:30
Faris Ansari
11ae5d6be4 formatting 2018-05-30 08:19:27 +05:30
Faris Ansari
2559ae3119 formatting 2018-05-30 08:18:01 +05:30
Faris Ansari
2987fcd302 Add shields 2018-05-30 08:15:53 +05:30
Faris Ansari
27cfc76a7e
Merge pull request #29 from frappe/cypress
Setup Cypress and tests
2018-05-30 08:12:03 +05:30
Faris Ansari
e02af2077c Add test command and travis.yml 2018-05-30 08:05:40 +05:30
Faris Ansari
2d1bb23cda Comment out "cell edit" test for now 2018-05-30 08:05:21 +05:30
Faris Ansari
c387c649a5 Basic Tests for Cell, Column and Row functionalities 2018-05-30 07:48:15 +05:30
Faris Ansari
4a8cdd0362 Merge remote-tracking branch 'upstream/master' into cypress 2018-05-30 05:52:24 +05:30
Faris Ansari
db82d7fff4 v0.0.8 2018-05-23 18:16:53 +05:30
Faris Ansari
b61f7fdcab [fix] sortIndicator 2018-05-23 18:14:50 +05:30
Faris Ansari
faeef3856c v0.0.7 2018-05-23 18:04:48 +05:30
Faris Ansari
e6a8cb8d7f Deactivate filter on esc key 2018-05-23 18:03:17 +05:30
Faris Ansari
f3a8b465ef bindMoveColumn on every call of refreshHeader 2018-05-23 17:52:17 +05:30
Faris Ansari
f9775ecdf2 Better implementation of setStyle
- handle overridding of styles by itself
2018-05-23 17:51:39 +05:30
Faris Ansari
737430671e Init Cypress 2018-05-22 13:48:18 +05:30
Faris Ansari
00b8000d49 v0.0.6 2018-05-18 12:23:35 +05:30
Faris Ansari
c61e53da8c [fix] headerDropdown option merge 2018-05-18 12:18:30 +05:30
Faris Ansari
a64c51bfdf Merge branch 'master' of https://github.com/frappe/datatable 2018-05-18 12:17:20 +05:30
Faris Ansari
4b2c98fe32
Fix Naming 2018-05-08 17:35:29 +05:30
59 changed files with 9518 additions and 14198 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -7,7 +7,8 @@
"env": {
"browser": true,
"es6": true,
"node": true
"node": true,
"mocha": true
},
"globals": {
@ -21,7 +22,9 @@
"it": true,
"expect": true,
"sinon": true,
"Clusterize": true
"Clusterize": true,
"cy": true,
"Cypress": true
},
"plugins": [
@ -32,7 +35,6 @@
"block-scoped-var": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": [2, { "properties": "always" }],
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,

24
.github/workflows/test-and-release.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Test and Release
on:
push:
branches:
- master
jobs:
test-and-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Lint, build and test
uses: cypress-io/github-action@v2
with:
build: yarn lint-and-build
start: yarn cy:server
record: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn semantic-release

16
.github/workflows/test-pull-request.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Test Pull Request
on: pull_request
jobs:
test-pull-request:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Lint, build and test
uses: cypress-io/github-action@v2
with:
build: yarn lint-and-build
start: yarn cy:server
record: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

9
.gitignore vendored
View File

@ -34,3 +34,12 @@ node_modules
npm-debug.log.*
.DS_Store
# cypress
cypress/screenshots
cypress/videos
# dist
dist
.env

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2018 Frappé Technologies Pvt. Ltd. <developers@frappe.io>
Copyright (c) 2018 Frappe Technologies Pvt. Ltd. <developers@frappe.io>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,39 +1,75 @@
<div align="center">
<img src="https://github.com/frappe/design/blob/master/logos/data-table-logo.svg" height="128">
<h2>Frappe DataTables</h2>
<p align="center">
<p>
A modern datatable library for the web
</p>
<div align="center" markdown="1">
<img width="80" alt="datatable-logo" src="https://github.com/user-attachments/assets/8235f4b9-993a-4329-97de-9431dcf63aae" >
<h1>Frappe DataTable</h1>
**A modern datatable library for the web**
[![Test and Release](https://github.com/frappe/datatable/workflows/Test%20and%20Release/badge.svg)](https://github.com/frappe/datatable/actions?query=workflow%3A%22Test+and+Release%22)
[![npm version](https://badge.fury.io/js/frappe-datatable.svg)](https://badge.fury.io/js/frappe-datatable)
![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/frappe-datatable.svg)
![datatable-demo-2](https://user-images.githubusercontent.com/9355208/40740030-5412aa40-6465-11e8-8542-b0247ab1daac.gif)
</div>
## Features
## Frappe Datatable
Frappe DataTable is a simple, modern and interactive datatable library for displaying tabular data. Originally built for [ERPNext](https://github.com/frappe/erpnext), it can be used to render large amount of rows without sacrificing performance and has the basic data grid features like inline editing and keyboard navigation. It does not require jQuery, unlike most data grids out there.
### Motivation
I was trying to remove all legacy UI components from the [frappe](https://github.com/frappe/frappe) codebase. We were using [SlickGrid](https://github.com/mleibman/SlickGrid) for rendering tables. It was unmaintained and UI was dated. Other datatable solutions either didn't have the features we needed or were closed source. So we built our own.
### Key Features
- **Cell**: Enable editing within individual cells and features like custom formatters, inline editing, and mouse selection. Users can easily copy cell content, navigate through cells using the keyboard, and take advantage of a custom cell editor for advanced functionality.
- **Column**: Columns are highly flexible, allowing users to reorder, resize, and sort them with ease. Additional features include hiding/removing columns and adding custom actions.
- **Row**: Rows support advanced interactions, including row selection, tree-structured organization, and inline filters for precise control. They handle large datasets efficiently with dynamic row heights.
* Resizable columns
* Sorting
* Custom formatted data
* In place editing
* Efficient rendering of large data
## Usage
```bash
yarn add frappe-datatable
# or
npm install frappe-datatable
```
> Note: [`sortablejs`](https://github.com/RubaXa/Sortable) is required to be installed as well.
```js
var grid = new DataTable(document.querySelector('#data-table'), {
data: {
columns: [ 'Sr No.', 'First Name', 'Last Name' ],
rows: [
[ '1', 'Don', 'Joe' ],
[ '2', 'Mary', 'Jane' ]
]
}
const datatable = new DataTable('#datatable', {
columns: [ 'First Name', 'Last Name', 'Position' ],
data: [
[ 'Don', 'Joe', 'Designer' ],
[ 'Mary', 'Jane', 'Software Developer' ]
]
});
```
## Contribute
## Development Setup
* `yarn start` - Start rollup watch
* `yarn build` - Build js/css bundles
* `yarn start` - Start dev server
* Open `index.html` located in the root folder, and start development.
* Run `yarn lint` before committing changes
* This project uses [commitizen](https://github.com/commitizen/cz-cli) for conventional commit messages, use `yarn commit` command instead of `git commit`
## License
## Links
MIT
- [Making a new datatable for the web](https://medium.com/frapp%C3%A9-thoughts/things-i-learned-building-a-library-for-the-web-6846a588bf53)
<br>
<br>
<div align="center" style="padding-top: 0.75rem;">
<a href="https://frappe.io" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/Frappe-white.png">
<img src="https://frappe.io/files/Frappe-black.png" alt="Frappe Technologies" height="28"/>
</picture>
</a>
</div>

4
cypress.json Normal file
View File

@ -0,0 +1,4 @@
{
"baseUrl": "http://localhost:8989",
"projectId": "2nsyux"
}

View File

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -0,0 +1,84 @@
describe('Cell', function () {
before(function () {
cy.visit('/');
});
it('focuses cell on click', function () {
cy.clickCell(2, 0)
.should('have.class', 'dt-cell--focus');
});
it('not focuses cell which are not focusable', function () {
cy.clickCell(1, 0)
.should('not.have.class', 'dt-cell--focus');
});
it('edit cell on enter press', function () {
cy.getCell(4, 0).type('{enter}')
.should('have.class', 'dt-cell--editing')
.type('{enter}')
.should('not.have.class', 'dt-cell--editing');
});
it('edit cell on double click', function () {
cy.getCell(4, 0)
.as('target')
.dblclick({ force: true })
.should('have.class', 'dt-cell--editing');
cy.clickCell(3, 0);
cy.get('@target').should('not.have.class', 'dt-cell--editing');
});
it('edit cell', function () {
cy.getCell(4, 1).dblclick({ force: true });
cy.getCell(4, 1).find('input').click();
cy.focused().type('{selectall}{del}Test{enter}');
cy.getCell(4, 1).contains('Test');
});
it('if editing is false: editing should not activate', function () {
cy.getCell(3, 0).dblclick({ force: true })
.should('not.have.class', 'dt-cell--editing');
});
it('navigation using arrow keys', function () {
cy.clickCell(2, 0)
.type('{rightarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--3-0')
.click({ force: true })
.type('{downarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--3-1');
// TODO: test navigation over hidden rows
});
it('navigation using ctrl + arrow keys', function () {
cy.clickCell(2, 0)
.type('{ctrl}{rightarrow}');
cy.get('.dt-cell--focus')
.should('have.class', 'dt-cell--11-0');
});
it('cell selection using shift + arrow keys', function () {
cy.getCell(2, 1)
.type('{shift}{rightarrow}{rightarrow}{downarrow}');
// 6 cells and 2 headers
cy.get('.dt-cell--highlight').should('have.length', 6 + 2);
cy.clickCell(2, 0);
});
it('mouse selection', function () {
// TODO:
// cy.getCell(2, 1)
// .trigger('mousedown', { which: 1, pageX: 331, pageY: 207, force: true })
// .trigger('mousemove', { which: 1, pageX: 489, pageY: 312 })
// .trigger('mouseup');
});
});

View File

@ -0,0 +1,70 @@
describe('Column', function () {
before(function () {
cy.visit('/');
});
it('header dropdown toggles on click', function () {
cy.getColumnCell(2)
.find('.dt-dropdown__toggle')
.as('toggle')
.click();
cy.get('.dt-dropdown__list')
.as('dropdown-list')
.should('be.visible');
cy.getColumnCell(2).click();
cy.get('@dropdown-list').should('not.be.visible');
});
it('sort ascending button should work', function () {
cy.clickDropdown(2);
cy.clickDropdownItem(2, 'Sort Ascending');
cy.window().then(win => win.datatable.getColumn(2))
.its('sortOrder')
.should('eq', 'asc');
cy.window().then(win => win.datatable.datamanager)
.its('currentSort.colIndex')
.should('eq', 2);
cy.get('.dt-scrollable .dt-row:first div:nth-of-type(3)')
.contains('Airi Satou');
cy.clickDropdownItem(2, 'Reset sorting');
});
it('removes column using dropdown action', function () {
cy.get('.dt-cell--header').should('have.length', 12);
cy.clickDropdown(5);
cy.clickDropdownItem(5, 'Remove column');
cy.get('.dt-cell--header').should('have.length', 11);
});
it('resize column with mouse drag', function () {
cy.get('.dt-cell--header-4 .dt-cell__resize-handle').as('resize-handle');
cy
.get('@resize-handle')
.trigger('mousedown')
.trigger('mousemove', { pageX: 700, pageY: 20, which: 1 })
.trigger('mouseup');
cy.getColumnCell(4).invoke('css', 'width').then((width) => {
cy.getColumnCell(4)
.should('have.css', 'width', width);
cy.getCell(4, 1)
.should('have.css', 'width', width);
});
});
it('resize column using double click', function () {
cy.get('.dt-cell--header-4 .dt-cell__resize-handle').trigger('dblclick');
cy.getColumnCell(4).should('have.css', 'width')
.and('match', /9\dpx/);
cy.getCell(4, 1).should('have.css', 'width')
.and('match', /9\dpx/);
});
});

View File

@ -0,0 +1,89 @@
describe('Inline Filters', function () {
before(function () {
cy.visit('/');
});
beforeEach(function () {
cy.get('.dt-filter[data-col-index=4]').as('filterInput4');
cy.get('.dt-filter[data-col-index=5]').as('filterInput5');
cy.get('.dt-filter[data-col-index=6]').as('filterInput6');
cy.get('.dt-row[data-row-index=0]').should('be.visible');
});
it('simple text filter', function () {
cy.getCell(4, 0).click().type('{ctrl}f');
cy.get('@filterInput4').type('edin');
cy.get('.dt-row-0').should('be.visible');
cy.get('.dt-row-1').should('not.exist');
cy.get('@filterInput4').clear();
});
it('simple number filter', function () {
cy.get('@filterInput5').type('2360');
cy.get('.dt-row[data-row-index=8]').should('be.visible');
cy.get('.dt-row[data-row-index=15]').should('not.exist');
cy.get('.dt-row[data-row-index=22]').should('not.exist');
cy.get('@filterInput5').clear();
});
it('greater than', function () {
cy.get('@filterInput5').type('> 6000');
cy.get('.dt-row[data-row-index=0]').should('not.exist');
cy.get('.dt-row[data-row-index=3]').should('be.visible');
cy.get('@filterInput5').clear();
});
it('less than', function () {
cy.get('@filterInput5').type('< 2000');
cy.get('.dt-row[data-row-index=0]').should('not.exist');
cy.get('.dt-row[data-row-index=51]').should('be.visible');
cy.get('@filterInput5').clear();
});
it('range', function () {
cy.get('@filterInput5').type(' 2000: 5000');
cy.get('.dt-row[data-row-index=4]').should('not.exist');
cy.get('.dt-row[data-row-index=5]').should('be.visible');
cy.get('@filterInput5').clear();
});
it('equals', function () {
cy.get('@filterInput5').type('=9608');
cy.get('.dt-row-6').should('be.visible');
cy.get('@filterInput5').clear();
});
it('multiple filters', function () {
cy.get('@filterInput4').type('to');
cy.get('@filterInput5').type('54');
cy.get('.dt-row[data-row-index=4]').should('be.visible');
cy.get('.dt-row[data-row-index=1]').should('not.exist');
cy.get('@filterInput4').clear();
cy.get('@filterInput5').clear();
});
it('greater than for string type filters', function () {
cy.get('@filterInput6').type('> 01/07/2011');
cy.get('.dt-row[data-row-index=0]').should('not.exist');
cy.get('.dt-row[data-row-index=1]').should('be.visible');
cy.get('.dt-row[data-row-index=3]').should('be.visible');
cy.get('.dt-row[data-row-index=5]').should('be.visible');
cy.get('@filterInput6').clear();
});
it('filters with sorting', function () {
cy.visit('/');
cy.clickDropdown(7);
cy.clickDropdownItem(7, 'Sort Descending');
cy.get('.dt-filter[data-col-index=5]').as('filterInput5');
cy.getCell(5, 24).click().type('{ctrl}f');
cy.get('@filterInput5').type('>3000', {delay: 100});
cy.get('.dt-scrollable .dt-row:first')
.should('contain', 'Angelica')
.should('have.class', 'dt-row-24');
cy.get('@filterInput5').clear();
});
});

View File

@ -0,0 +1,20 @@
describe('DataTable init', function () {
it('instance is created without any errors', () => {
cy.visit('/');
cy.window()
.its('DataTable')
.then(DataTable => {
// eslint-disable-next-line
new DataTable('#datatable2', {
columns: ['Name', 'Position'],
data: [
['Faris', 'Developer']
]
});
});
cy.get('#datatable2 .datatable')
.contains('Faris');
});
});

View File

@ -0,0 +1,15 @@
describe('Row', function () {
before(function () {
cy.visit('/');
});
it('check / uncheck row', function () {
cy.get('.dt-scrollable .dt-row:first')
.find('input[type="checkbox"]')
.click();
cy.get('[data-row-index="0"]').should('have.class', 'dt-row--highlight');
cy.get('.dt-toast').contains('1 row selected');
});
});

17
cypress/plugins/index.js Normal file
View File

@ -0,0 +1,17 @@
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
};

View File

@ -0,0 +1,57 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
Cypress.Commands.add('getCell', (col, row) => {
return cy.get(`.dt-cell--${col}-${row}`);
});
Cypress.Commands.add('clickCell', (col, row) => {
return cy.getCell(col, row).click({ force: true });
});
Cypress.Commands.add('getColumnCell', (col) => {
return cy.get(`.dt-cell--header-${col}`);
});
Cypress.Commands.add('clickDropdown', (col) => {
return cy.getColumnCell(col)
.find('.dt-dropdown__toggle')
.click();
});
Cypress.Commands.add('clickDropdownItem', (col, item) => {
return cy.get(`.dt-dropdown__list-item:contains("${item}")`)
.click({ force: true });
});
Cypress.Commands.add('typeTab', (shiftKey, ctrlKey) => {
cy.focused().trigger('keydown', {
keyCode: 9,
which: 9,
shiftKey: shiftKey,
ctrlKey: ctrlKey
});
});

20
cypress/support/index.js Normal file
View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

12
cypress/tsconfig.json Normal file
View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"allowJs": true,
"baseUrl": "../node_modules",
"types": [
"cypress"
]
},
"include": [
"**/*.*"
]
}

View File

@ -1,282 +0,0 @@
/* This file is processed by postcss */
/* variables */
.data-table {
/* styling */
position: relative;
overflow: auto;
}
/* resets */
.data-table *, .data-table *::after, .data-table *::before {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.data-table button, .data-table input {
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
}
.data-table .input-style {
outline: none;
width: 100%;
border: none;
}
.data-table *, .data-table *:focus {
outline: none;
border-radius: 0px;
-webkit-box-shadow: none;
box-shadow: none;
}
.data-table table {
border-collapse: collapse;
}
.data-table table td {
padding: 0;
border: 1px solid #d1d8dd;
}
.data-table thead td {
border-bottom-width: 1px;
}
.data-table .freeze-container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #f5f7fa;
opacity: 0.5;
font-size: 2em;
}
.data-table .freeze-container span {
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.data-table .hide {
display: none;
}
.data-table .toast-message {
position: absolute;
bottom: 16px;
bottom: 1rem;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
.data-table .toast-message span {
display: inline-block;
background-color: rgba(0, 0, 0, .8);
color: #dfe2e5;
border-radius: 3px;
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.body-scrollable {
max-height: 500px;
overflow: auto;
border-bottom: 1px solid #d1d8dd;
}
.body-scrollable.row-highlight-all .data-table-row:not(.row-unhighlight) {
background-color: #f5f7fa;
}
.data-table-header {
position: absolute;
top: 0;
left: 0;
background-color: white;
font-weight: bold;
}
.data-table-header .content span:not(.column-resizer) {
cursor: pointer;
}
.data-table-header .column-resizer {
display: none;
position: absolute;
right: 0;
top: 0;
width: 4px;
width: 0.25rem;
height: 100%;
background-color: rgb(82, 146, 247);
cursor: col-resize;
}
.data-table-header .data-table-dropdown {
position: absolute;
right: 10px;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
vertical-align: top;
text-align: left;
}
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-list {
display: block;
}
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-toggle {
display: block;
}
.data-table-header .data-table-dropdown-toggle {
display: none;
background-color: transparent;
border: none;
}
.data-table-header .data-table-dropdown-list {
display: none;
font-weight: normal;
position: absolute;
min-width: 128px;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: white;
border-radius: 3px;
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
padding-bottom: 8px;
padding-bottom: 0.5rem;
padding-top: 8px;
padding-top: 0.5rem;
}
.data-table-header .data-table-dropdown-list> div {
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.data-table-header .data-table-dropdown-list> div:hover {
background-color: #f5f7fa;
}
.data-table-header .data-table-cell.remove-column {
background-color: #FD8B8B;
-webkit-transition: 300ms background-color ease-in-out;
transition: 300ms background-color ease-in-out;
}
.data-table-header .data-table-cell.sortable-chosen {
background-color: #f5f7fa;
}
.data-table-cell {
position: relative;
}
.data-table-cell .content {
padding: 8px;
padding: 0.5rem;
border: 2px solid transparent;
}
.data-table-cell .content.ellipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.data-table-cell .edit-cell {
display: none;
padding: 8px;
padding: 0.5rem;
background: #fff;
z-index: 1;
height: 100%;
}
.data-table-cell.selected .content {
border: 2px solid rgb(82, 146, 247);
}
.data-table-cell.editing .content {
display: none;
}
.data-table-cell.editing .edit-cell {
border: 2px solid rgb(82, 146, 247);
display: block;
}
.data-table-cell.highlight {
background-color: #f5f7fa;
}
.data-table-cell:hover .column-resizer {
display: inline-block;
}
.data-table-cell:hover .data-table-dropdown-toggle {
display: block;
}
.data-table-cell .tree-node {
display: inline-block;
position: relative;
}
.data-table-cell .toggle {
display: inline-block;
position: absolute;
padding: 0 4px;
cursor: pointer;
}
.data-table-cell .toggle:before {
content: '▼';
}
.data-table-cell.tree-close .toggle:before {
content: '►';
}
.data-table-row.row-highlight {
background-color: #f5f7fa;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
body.data-table-resize {
cursor: col-resize;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,252 +0,0 @@
.datatable *, .datatable *::after, .datatable *::before {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.datatable {
position: relative;
overflow: auto;
}
.dt-header {
border-collapse: collapse;
border-bottom: 1px solid #d1d8dd;
position: absolute;
top: 0;
left: 0;
background-color: #fff;
}
.dt-body {
border-collapse: collapse;
}
.dt-scrollable {
max-height: 40vw;
overflow: auto;
border-bottom: 1px solid #d1d8dd;
}
.dt-scrollable--highlight-all {
background-color: #fffce7;
}
.dt-scrollable__no-data {
text-align: center;
padding: 16px;
padding: 1rem;
border-left: 1px solid #d1d8dd;
border-right: 1px solid #d1d8dd;
}
.dt-row--highlight {
background-color: #fffce7;
}
.dt-row--unhighlight {
background-color: #fff;
}
.dt-row--hide {
display: none;
}
.dt-cell {
border: 1px solid #d1d8dd;
position: relative;
outline: none;
padding: 0;
}
.dt-cell__content {
padding: 8px;
padding: 0.5rem;
border: 2px solid transparent;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.dt-cell__edit {
display: none;
padding: 8px;
padding: 0.5rem;
background-color: #fff;
border: 2px solid rgb(255, 160, 10);
z-index: 1;
height: 100%;
}
.dt-cell__resize-handle {
opacity: 0;
position: absolute;
right: -3px;
top: 0;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 1;
}
.dt-cell--editing .dt-cell__content {
display: none;
}
.dt-cell--editing .dt-cell__edit {
display: block;
}
.dt-cell--focus .dt-cell__content {
border-color: rgb(82, 146, 247);
}
.dt-cell--highlight {
background-color: #f5f7fa;
}
.dt-cell--dragging {
background-color: #f5f7fa;
}
.dt-cell--header .dt-cell__content {
padding-right: 16px;
padding-right: 1rem;
font-weight: bold;
}
.dt-cell--header:hover .dt-dropdown__toggle {
opacity: 1;
}
.dt-cell--tree-close .dt-tree-node__toggle:before {
content: '►';
}
.dt-dropdown {
position: absolute;
right: 10px;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
vertical-align: top;
text-align: left;
font-weight: normal;
cursor: pointer;
}
.dt-dropdown__toggle {
opacity: 0;
}
.dt-dropdown__list {
display: none;
position: absolute;
min-width: 128px;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: #fff;
border-radius: 3px;
padding: 8px 0;
padding: 0.5rem 0;
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
}
.dt-dropdown__list-item {
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.dt-dropdown__list-item:hover {
background-color: #f5f7fa;
}
.dt-dropdown--active .dt-dropdown__list {
display: block;
}
.dt-tree-node {
display: inline-block;
position: relative;
}
.dt-tree-node__toggle {
display: inline-block;
position: absolute;
font-size: 10px;
padding: 0 4px;
cursor: pointer;
}
.dt-tree-node__toggle:before {
content: '▼';
}
.dt-toast {
position: absolute;
bottom: 16px;
bottom: 1rem;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
.dt-toast__message {
display: inline-block;
background-color: rgba(0, 0, 0, .8);
color: #dfe2e5;
border-radius: 3px;
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.dt-input {
outline: none;
width: 100%;
border: none;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
}
.dt-freeze {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #f5f7fa;
opacity: 0.5;
font-size: 2em;
}
.dt-freeze__message {
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
body.dt-resize {
cursor: col-resize;
}

3623
dist/frappe-datatable.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
.data-table{position:relative;overflow:auto}.data-table *,.data-table :after,.data-table :before{-webkit-box-sizing:border-box;box-sizing:border-box}.data-table button,.data-table input{overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit;margin:0;padding:0}.data-table .input-style{outline:none;width:100%;border:none}.data-table *,.data-table :focus{outline:none;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.data-table table{border-collapse:collapse}.data-table table td{padding:0;border:1px solid #d1d8dd}.data-table thead td{border-bottom-width:1px}.data-table .freeze-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center;position:absolute;left:0;right:0;top:0;bottom:0;background-color:#f5f7fa;opacity:.5;font-size:2em}.data-table .freeze-container span{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.data-table .hide{display:none}.data-table .toast-message{position:absolute;bottom:16px;bottom:1rem;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.data-table .toast-message span{display:inline-block;background-color:rgba(0,0,0,.8);color:#dfe2e5;border-radius:3px;padding:8px 16px;padding:.5rem 1rem}.body-scrollable{max-height:500px;overflow:auto;border-bottom:1px solid #d1d8dd}.body-scrollable.row-highlight-all .data-table-row:not(.row-unhighlight){background-color:#f5f7fa}.body-scrollable .no-data td{text-align:center;padding:8px;padding:.5rem}.data-table-header{position:absolute;top:0;left:0;background-color:#fff;font-weight:700}.data-table-header .content span:not(.column-resizer){cursor:pointer}.data-table-header .column-resizer{display:none;position:absolute;right:0;top:0;width:4px;width:.25rem;height:100%;background-color:#5292f7;cursor:col-resize}.data-table-header .data-table-dropdown{position:absolute;right:10px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:top;text-align:left}.data-table-header .data-table-dropdown.is-active .data-table-dropdown-list,.data-table-header .data-table-dropdown.is-active .data-table-dropdown-toggle{display:block}.data-table-header .data-table-dropdown-toggle{display:none;background-color:transparent;border:none}.data-table-header .data-table-dropdown-list{display:none;font-weight:400;position:absolute;min-width:128px;min-width:8rem;top:100%;right:0;z-index:1;background-color:#fff;border-radius:3px;-webkit-box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);padding-bottom:8px;padding-bottom:.5rem;padding-top:8px;padding-top:.5rem}.data-table-header .data-table-dropdown-list>div{padding:8px 16px;padding:.5rem 1rem}.data-table-header .data-table-dropdown-list>div:hover{background-color:#f5f7fa}.data-table-header .data-table-cell.remove-column{background-color:#fd8b8b;-webkit-transition:background-color .3s ease-in-out;transition:background-color .3s ease-in-out}.data-table-header .data-table-cell.sortable-chosen{background-color:#f5f7fa}.data-table-cell{position:relative}.data-table-cell .content{padding:8px;padding:.5rem;border:2px solid transparent;height:100%}.data-table-cell .content.ellipsis{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.data-table-cell .edit-cell{display:none;padding:8px;padding:.5rem;background-color:#fff;z-index:1;height:100%}.data-table-cell.selected .content{border:2px solid #5292f7}.data-table-cell.editing .content{display:none}.data-table-cell.editing .edit-cell{border:2px solid #ffa00a;border-style:dashed;display:block}.data-table-cell.highlight{background-color:#f5f7fa}.data-table-cell:hover .column-resizer{display:inline-block}.data-table-cell:hover .data-table-dropdown-toggle{display:block}.data-table-cell .tree-node{display:inline-block;position:relative}.data-table-cell .toggle{display:inline-block;position:absolute;padding:0 4px;cursor:pointer}.data-table-cell .toggle:before{content:"▼"}.data-table-cell.tree-close .toggle:before{content:"►"}.data-table-row.row-highlight{background-color:#f5f7fa}.noselect{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.data-table-resize{cursor:col-resize}

File diff suppressed because one or more lines are too long

View File

@ -1,289 +0,0 @@
/* This file is processed by postcss */
/* variables */
.data-table {
/* styling */
position: relative;
overflow: auto;
}
/* resets */
.data-table *, .data-table *::after, .data-table *::before {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.data-table button, .data-table input {
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
}
.data-table .input-style {
outline: none;
width: 100%;
border: none;
}
.data-table *, .data-table *:focus {
outline: none;
border-radius: 0px;
-webkit-box-shadow: none;
box-shadow: none;
}
.data-table table {
border-collapse: collapse;
}
.data-table table td {
padding: 0;
border: 1px solid #d1d8dd;
}
.data-table thead td {
border-bottom-width: 1px;
}
.data-table .freeze-container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #f5f7fa;
opacity: 0.5;
font-size: 2em;
}
.data-table .freeze-container span {
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.data-table .hide {
display: none;
}
.data-table .toast-message {
position: absolute;
bottom: 16px;
bottom: 1rem;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
.data-table .toast-message span {
display: inline-block;
background-color: rgba(0, 0, 0, .8);
color: #dfe2e5;
border-radius: 3px;
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.body-scrollable {
max-height: 500px;
overflow: auto;
border-bottom: 1px solid #d1d8dd;
}
.body-scrollable.row-highlight-all .data-table-row:not(.row-unhighlight) {
background-color: #f5f7fa;
}
.body-scrollable .no-data td {
text-align: center;
padding: 8px;
padding: 0.5rem;
}
.data-table-header {
position: absolute;
top: 0;
left: 0;
background-color: white;
font-weight: bold;
}
.data-table-header .content span:not(.column-resizer) {
cursor: pointer;
}
.data-table-header .column-resizer {
display: none;
position: absolute;
right: 0;
top: 0;
width: 4px;
width: 0.25rem;
height: 100%;
background-color: rgb(82, 146, 247);
cursor: col-resize;
}
.data-table-header .data-table-dropdown {
position: absolute;
right: 10px;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
vertical-align: top;
text-align: left;
}
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-list {
display: block;
}
.data-table-header .data-table-dropdown.is-active .data-table-dropdown-toggle {
display: block;
}
.data-table-header .data-table-dropdown-toggle {
display: none;
background-color: transparent;
border: none;
}
.data-table-header .data-table-dropdown-list {
display: none;
font-weight: normal;
position: absolute;
min-width: 128px;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: white;
border-radius: 3px;
-webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
box-shadow: 0 2px 3px rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .1);
padding-bottom: 8px;
padding-bottom: 0.5rem;
padding-top: 8px;
padding-top: 0.5rem;
}
.data-table-header .data-table-dropdown-list> div {
padding: 8px 16px;
padding: 0.5rem 1rem;
}
.data-table-header .data-table-dropdown-list> div:hover {
background-color: #f5f7fa;
}
.data-table-header .data-table-cell.remove-column {
background-color: #FD8B8B;
-webkit-transition: 300ms background-color ease-in-out;
transition: 300ms background-color ease-in-out;
}
.data-table-header .data-table-cell.sortable-chosen {
background-color: #f5f7fa;
}
.data-table-cell {
position: relative;
}
.data-table-cell .content {
padding: 8px;
padding: 0.5rem;
border: 2px solid transparent;
height: 100%;
}
.data-table-cell .content.ellipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.data-table-cell .edit-cell {
display: none;
padding: 8px;
padding: 0.5rem;
background-color: #fff;
z-index: 1;
height: 100%;
}
.data-table-cell.selected .content {
border: 2px solid rgb(82, 146, 247);
}
.data-table-cell.editing .content {
display: none;
}
.data-table-cell.editing .edit-cell {
border: 2px solid rgb(255, 160, 10);
display: block;
}
.data-table-cell.highlight {
background-color: #f5f7fa;
}
.data-table-cell:hover .column-resizer {
display: inline-block;
}
.data-table-cell:hover .data-table-dropdown-toggle {
display: block;
}
.data-table-cell .tree-node {
display: inline-block;
position: relative;
}
.data-table-cell .toggle {
display: inline-block;
position: absolute;
padding: 0 4px;
cursor: pointer;
}
.data-table-cell .toggle:before {
content: '▼';
}
.data-table-cell.tree-close .toggle:before {
content: '►';
}
.data-table-row.row-highlight {
background-color: #f5f7fa;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
body.data-table-resize {
cursor: col-resize;
}

View File

@ -1,99 +0,0 @@
/*
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #F0F0F0;
}
/* Base color: saturation 0; */
.hljs,
.hljs-subst {
color: #444;
}
.hljs-comment {
color: #888888;
}
.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}
/* User color: hue: 0 */
.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #880000;
}
.hljs-title,
.hljs-section {
color: #880000;
font-weight: bold;
}
.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #BC6060;
}
/* Language color: hue: 90; */
.hljs-literal {
color: #78A960;
}
.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #397300;
}
/* Meta color: hue: 200 */
.hljs-meta {
color: #1f7199;
}
.hljs-meta-string {
color: #4d99bf;
}
/* Misc effects */
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

View File

@ -1,159 +0,0 @@
@import url('https://fonts.googleapis.com/css?family=Lato:400,700,900');
:root {
--border-color: #d1d8dd;
--border: 1px solid var(--border-color);
--text-color: #36414C;
--navbar-font-size: 1.25rem;
--light-bg: #f5f7fa;
--primary-color: #7575ff;
--primary-color-hover: #5b5be5;
}
*, *::after, *::before {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html, body {
/* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; */
font-family: Lato, sans-serif;
font-size: 12px;
color: var(--text-color);
}
body {
position: relative;
}
code {
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
h1 {
font-size: 2.5rem;
margin: 2rem 0 4rem 0;
}
h3 {
font-size: 1.25rem;
}
p {
font-size: 1.25rem;
margin: 1rem 0;
}
.navbar {
position: sticky;
top: 0;
padding: 1.5rem;
background-color: white;
z-index: 1;
border-bottom: var(--border);
}
.navbar .container {
display: flex;
justify-content: space-between;
}
.navbar-links a {
color: var(--text-color);
text-decoration: none;
font-weight: bold;
font-size: var(--navbar-font-size);
}
.container {
max-width: 720px;
margin: 0 auto;
}
.logo {
max-width: 8rem;
}
.footer-logo {
max-width: 3rem;
max-height: 3rem;
padding: 0.5rem;
background-color: #fff;
transform: translateY(-50%);
}
.text-center {
text-align: center;
}
.padding-1 {
padding: 0.5rem;
}
.showcase {
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 4rem 0;
}
.showcase.align-center {
align-items: center;
}
.features {
display: flex;
width: 100%;
font-size: 1.25rem;
margin-top: 1rem;
line-height: 2rem;
}
.features ul {
padding-left: 2rem;
flex: 1;
}
[class^="example-"] {
display: flex;
justify-content: center;
width: 100%;
}
.code {
text-align: left;
width: 100%;
margin: 1rem 0;
}
.hljs {
padding: 2rem;
border-left: 2px solid var(--border-color);
background-color: var(--light-bg);
}
footer {
border-top: 1px solid #d1d8dd;
}
.download-button {
color: #fff;
background-color: var(--primary-color);
border: 0px;
border-bottom: 3px solid rgba(0, 0, 0, 0.2);
font-size: 1.5rem;
padding: 1rem 2rem;
border-radius: 3px;
}
.download-button:hover {
cursor: pointer;
background-color: var(--primary-color-hover);
}
/* .data-table-header {
background: #f7fafc;
color: #8d99a6;
} */

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="340.42303" height="330.00024" viewBox="0 0 90.070259 87.312561" version="1.1" id="svg3970" inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<defs id="defs3964"/>
<metadata id="metadata3967">
</metadata>
<g inkscape:label="Layer 1" id="layer1" transform="translate(-7.1255851,-173.21277)">
<rect transform="scale(-1,1)" ry="13.229167" rx="13.229167" y="173.21277" x="-94.438156" height="87.312546" width="87.312553" id="rect2004" style="opacity:1;fill:#edfdff;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0 96.25 L 0 151.25 L 77.5 151.25 C 80.27 151.25 82.5 149.02 82.5 146.25 L 82.5 101.25 C 82.5 98.48 80.27 96.25 77.5 96.25 L 0 96.25 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2006"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 50 0 C 22.299998 0 1.4210855e-14 22.299998 0 50 L 0 54.5 L 78 54.5 C 80.77 54.5 83 52.27 83 49.5 L 83 4.5 C 83 2.503655 81.832377 0.80274423 80.148438 0 L 50 0 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2008"/>
<rect style="opacity:1;fill:#71caff;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" id="rect2010" width="57.195782" height="14.552094" x="40.000061" y="198.74506" ry="1.3229167" rx="1.3229166"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0 193 L 0 248 L 77.5 248 C 80.27 248 82.5 245.77 82.5 243 L 82.5 198 C 82.5 195.23 80.27 193 77.5 193 L 0 193 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2012"/>
<path style="fill:#87e34c;fill-opacity:1;stroke:none;stroke-width:1.00000012px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" clip-path="none" d="M 0 219.61328 L 0 284.29688 C 0 309.61626 27.643317 330 61.980469 330 L 268.01953 330 C 300.54576 330 327.04905 311.70603 329.75586 288.25586 L 330 248.5 C 256.7254 233.20714 83.19117 220.5514 0 219.61328 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="path2020"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 129.25 193.5 C 126.48 193.5 124.25 195.73 124.25 198.5 L 124.25 243.5 C 124.25 246.27 126.48 248.5 129.25 248.5 L 330 248.5 L 330 193.5 L 129.25 193.5 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2014"/>
<path style="opacity:1;fill:#c3f1ff;fill-opacity:1;stroke:none;stroke-width:6.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 127.10156 0 C 125.41762 0.80274423 124.25 2.503655 124.25 4.5 L 124.25 49.5 C 124.25 52.27 126.48 54.5 129.25 54.5 L 330 54.5 L 330 50 C 330 22.299998 307.7 0 280 0 L 127.10156 0 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect2016"/>
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 129.25 290.5 C 126.48 290.5 124.25 292.73 124.25 295.5 L 124.25 330 L 280 330 C 304.09518 330 324.09664 313.12363 328.89648 290.5 L 129.25 290.5 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect1135"/>
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:6.59999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="M 0.94921875 289.75 C 5.4614968 312.74994 25.639123 330 50 330 L 82.5 330 L 82.5 294.75 C 82.5 291.98 80.27 289.75 77.5 289.75 L 0.94921875 289.75 z " transform="matrix(0.26458332,0,0,0.26458332,7.1255851,173.21277)" id="rect1139"/>
<path style="opacity:1;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:1.74624979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;" clip-path="none" d="m 7.1257773,231.31877 v 7.51065 H 27.630987 c 0.732896,0 1.322917,-0.59002 1.322917,-1.32292 v -5.36505 C 20.620295,231.67818 13.007809,231.3851 7.1257773,231.31877 Z" id="path1264"/>
<path style="fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" clip-path="none" d="m 40.000259,232.82824 v 4.81056 c 0,0.73289 0.59002,1.32291 1.322916,1.32291 h 53.115108 c -11.982795,-2.50088 -34.095004,-4.73297 -54.438024,-6.13347 z" id="path1163"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100%"
height="100%"
viewBox="0 0 260 260"
version="1.1"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
id="svg16"
sodipodi:docname="frappe-bird-grey.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata22"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs20" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1029"
id="namedview18"
showgrid="false"
inkscape:snap-global="false"
inkscape:zoom="2.5673415"
inkscape:cx="125.36812"
inkscape:cy="132.66533"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="svg16" /><path
inkscape:connector-curvature="0"
id="path846"
d="m 39.426993,41.26578 c -0.08308,-0.002 -0.162537,0.01476 -0.244177,0.01847 -0.195406,-0.01104 -0.394663,-0.0078 -0.595581,0.02219 -0.671494,0.09826 -1.359798,0.439289 -1.985268,1.097637 L 36.166705,42.984362 3.911539,86.00785 c -1.4521873,1.95837 -0.089029,5.089411 2.840468,4.758942 l 35.051779,-4.999463 7.486826,52.499391 c 0.143332,1.06941 0.708175,1.87792 1.45651,2.37853 0.06099,0.57446 0.300326,1.15825 0.786491,1.67605 l 54.221237,55.16042 c 0.35241,0.34946 0.76163,0.57974 1.18925,0.71967 l 43.2449,43.9932 c 2.19797,2.06517 5.73881,0.59111 5.73827,-2.44537 l 0.72154,-88.30898 100.76861,-99.226182 c 2.03273,-1.933848 0.52846,-5.096585 -1.98911,-5.01476 L 146.21842,46.315471 c -1.15721,-0.0078 -1.82288,0.589677 -2.08644,0.84183 L 70.489255,119.67376 95.234723,86.666413 c 1.320352,-1.808803 0.561745,-3.846193 -0.6872,-4.787579 L 41.349464,41.92492 C 40.706305,41.485816 40.049657,41.2839 39.427179,41.266357 Z"
style="fill:#29344a;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,18 +0,0 @@
/*
Clusterize.js - v0.18.0 - 2017-11-04
http://NeXTs.github.com/Clusterize.js/
Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */
;(function(q,n){"undefined"!=typeof module?module.exports=n():"function"==typeof define&&"object"==typeof define.amd?define(n):this[q]=n()})("Clusterize",function(){function q(b,a,c){return a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)}function n(b,a,c){return a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent("on"+b,c)}function r(b){return"[object Array]"===Object.prototype.toString.call(b)}function m(b,a){return window.getComputedStyle?window.getComputedStyle(a)[b]:
a.currentStyle[b]}var l=function(){for(var b=3,a=document.createElement("b"),c=a.all||[];a.innerHTML="\x3c!--[if gt IE "+ ++b+"]><i><![endif]--\x3e",c[0];);return 4<b?b:document.documentMode}(),x=navigator.platform.toLowerCase().indexOf("mac")+1,p=function(b){if(!(this instanceof p))return new p(b);var a=this,c={rows_in_block:50,blocks_in_cluster:4,tag:null,show_no_data_row:!0,no_data_class:"clusterize-no-data",no_data_text:"No data",keep_parity:!0,callbacks:{}};a.options={};for(var d="rows_in_block blocks_in_cluster show_no_data_row no_data_class no_data_text keep_parity tag callbacks".split(" "),
f=0,h;h=d[f];f++)a.options[h]="undefined"!=typeof b[h]&&null!=b[h]?b[h]:c[h];c=["scroll","content"];for(f=0;d=c[f];f++)if(a[d+"_elem"]=b[d+"Id"]?document.getElementById(b[d+"Id"]):b[d+"Elem"],!a[d+"_elem"])throw Error("Error! Could not find "+d+" element");a.content_elem.hasAttribute("tabindex")||a.content_elem.setAttribute("tabindex",0);var e=r(b.rows)?b.rows:a.fetchMarkup(),g={};b=a.scroll_elem.scrollTop;a.insertToDOM(e,g);a.scroll_elem.scrollTop=b;var k=!1,m=0,l=!1,t=function(){x&&(l||(a.content_elem.style.pointerEvents=
"none"),l=!0,clearTimeout(m),m=setTimeout(function(){a.content_elem.style.pointerEvents="auto";l=!1},50));k!=(k=a.getClusterNum())&&a.insertToDOM(e,g);a.options.callbacks.scrollingProgress&&a.options.callbacks.scrollingProgress(a.getScrollProgress())},u=0,v=function(){clearTimeout(u);u=setTimeout(a.refresh,100)};q("scroll",a.scroll_elem,t);q("resize",window,v);a.destroy=function(b){n("scroll",a.scroll_elem,t);n("resize",window,v);a.html((b?a.generateEmptyRow():e).join(""))};a.refresh=function(b){(a.getRowsHeight(e)||
b)&&a.update(e)};a.update=function(b){e=r(b)?b:[];b=a.scroll_elem.scrollTop;e.length*a.options.item_height<b&&(k=a.scroll_elem.scrollTop=0);a.insertToDOM(e,g);a.scroll_elem.scrollTop=b};a.clear=function(){a.update([])};a.getRowsAmount=function(){return e.length};a.getScrollProgress=function(){return this.options.scroll_top/(e.length*this.options.item_height)*100||0};var w=function(b,c){var d=r(c)?c:[];d.length&&(e="append"==b?e.concat(d):d.concat(e),a.insertToDOM(e,g))};a.append=function(a){w("append",
a)};a.prepend=function(a){w("prepend",a)}};p.prototype={constructor:p,fetchMarkup:function(){for(var b=[],a=this.getChildNodes(this.content_elem);a.length;)b.push(a.shift().outerHTML);return b},exploreEnvironment:function(b,a){var c=this.options;c.content_tag=this.content_elem.tagName.toLowerCase();b.length&&(l&&9>=l&&!c.tag&&(c.tag=b[0].match(/<([^>\s/]*)/)[1].toLowerCase()),1>=this.content_elem.children.length&&(a.data=this.html(b[0]+b[0]+b[0])),c.tag||(c.tag=this.content_elem.children[0].tagName.toLowerCase()),
this.getRowsHeight(b))},getRowsHeight:function(b){var a=this.options,c=a.item_height;a.cluster_height=0;if(b.length){b=this.content_elem.children;var d=b[Math.floor(b.length/2)];a.item_height=d.offsetHeight;"tr"==a.tag&&"collapse"!=m("borderCollapse",this.content_elem)&&(a.item_height+=parseInt(m("borderSpacing",this.content_elem),10)||0);"tr"!=a.tag&&(b=parseInt(m("marginTop",d),10)||0,d=parseInt(m("marginBottom",d),10)||0,a.item_height+=Math.max(b,d));a.block_height=a.item_height*a.rows_in_block;
a.rows_in_cluster=a.blocks_in_cluster*a.rows_in_block;a.cluster_height=a.blocks_in_cluster*a.block_height;return c!=a.item_height}},getClusterNum:function(){this.options.scroll_top=this.scroll_elem.scrollTop;return Math.floor(this.options.scroll_top/(this.options.cluster_height-this.options.block_height))||0},generateEmptyRow:function(){var b=this.options;if(!b.tag||!b.show_no_data_row)return[];var a=document.createElement(b.tag),c=document.createTextNode(b.no_data_text);a.className=b.no_data_class;
if("tr"==b.tag){var d=document.createElement("td");d.colSpan=100;d.appendChild(c)}a.appendChild(d||c);return[a.outerHTML]},generate:function(b,a){var c=this.options,d=b.length;if(d<c.rows_in_block)return{top_offset:0,bottom_offset:0,rows_above:0,rows:d?b:this.generateEmptyRow()};var f=Math.max((c.rows_in_cluster-c.rows_in_block)*a,0),h=f+c.rows_in_cluster,e=Math.max(f*c.item_height,0);c=Math.max((d-h)*c.item_height,0);d=[];var g=f;for(1>e&&g++;f<h;f++)b[f]&&d.push(b[f]);return{top_offset:e,bottom_offset:c,
rows_above:g,rows:d}},renderExtraTag:function(b,a){var c=document.createElement(this.options.tag);c.className=["clusterize-extra-row","clusterize-"+b].join(" ");a&&(c.style.height=a+"px");return c.outerHTML},insertToDOM:function(b,a){this.options.cluster_height||this.exploreEnvironment(b,a);var c=this.generate(b,this.getClusterNum()),d=c.rows.join(""),f=this.checkChanges("data",d,a),h=this.checkChanges("top",c.top_offset,a),e=this.checkChanges("bottom",c.bottom_offset,a),g=this.options.callbacks,
k=[];f||h?(c.top_offset&&(this.options.keep_parity&&k.push(this.renderExtraTag("keep-parity")),k.push(this.renderExtraTag("top-space",c.top_offset))),k.push(d),c.bottom_offset&&k.push(this.renderExtraTag("bottom-space",c.bottom_offset)),g.clusterWillChange&&g.clusterWillChange(),this.html(k.join("")),"ol"==this.options.content_tag&&this.content_elem.setAttribute("start",c.rows_above),this.content_elem.style["counter-increment"]="clusterize-counter "+(c.rows_above-1),g.clusterChanged&&g.clusterChanged()):
e&&(this.content_elem.lastChild.style.height=c.bottom_offset+"px")},html:function(b){var a=this.content_elem;if(l&&9>=l&&"tr"==this.options.tag){var c=document.createElement("div");for(c.innerHTML="<table><tbody>"+b+"</tbody></table>";b=a.lastChild;)a.removeChild(b);for(c=this.getChildNodes(c.firstChild.firstChild);c.length;)a.appendChild(c.shift())}else a.innerHTML=b},getChildNodes:function(b){b=b.children;for(var a=[],c=0,d=b.length;c<d;c++)a.push(b[c]);return a},checkChanges:function(b,a,c){var d=
a!=c[b];c[b]=a;return d}};return p});

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,718 +0,0 @@
/* global DataTable */
/* eslint-disable no-unused-vars */
const {
columns,
data
} = getSampleData();
// Hero
let datatable1 = new DataTable('.example-1', {
columns,
data,
checkboxColumn: true
});
// // Formatted Cells
// let datatable2 = new DataTable('.example-2', {
// columns: ['Name', 'Position', 'Office', 'Extn.', 'Start Date',
// { content: 'Salary', format: val => '$' + val, align: 'right' }],
// data
// });
// // Inline Filters
// let datatable3 = new DataTable('.example-3', {
// columns,
// data,
// enableInlineFilters: true
// });
// datatable3.showToastMessage('Click on a cell and press Ctrl/Cmd + F');
// // Keyboard
// let datatable4 = new DataTable('.example-4', {
// columns,
// data
// });
// datatable4.showToastMessage('Double click to edit');
// // Tree Structured Rows
// let datatable5 = new DataTable('.example-5', getTreeData());
// datatable5.showToastMessage('Expand/Collapse tree nodes');
function getSampleData(multiplier) {
let columns = ['Name', 'Position', 'Office', {name: 'Extn.', width: 120}, 'Start Date', 'Salary'];
let data = [
['Tiger Nixon', 'System Architect', 'Edinburgh', 5421, '2011/04/25', '320,800'],
['Garrett Winters', 'Accountant', 'Tokyo', 8422, '2011/07/25', '170,750'],
['Ashton Cox', 'Junior Technical Author', 'San Francisco', 1562, '2009/01/12', '86,000'],
['Cedric Kelly', 'Senior Javascript Developer', 'Edinburgh', 6224, '2012/03/29', '433,060'],
['Airi Satou', 'Accountant', 'Tokyo', 5407, '2008/11/28', '162,700'],
['Brielle Williamson', 'Integration Specialist', 'New York', 4804, '2012/12/02', '372,000'],
['Herrod Chandler', 'Sales Assistant', 'San Francisco', 9608, '2012/08/06', '137,500'],
['Rhona Davidson', 'Integration Specialist', 'Tokyo', 6200, '2010/10/14', '327,900'],
['Colleen Hurst', 'Javascript Developer', 'San Francisco', 2360, '2009/09/15', '205,500'],
['Sonya Frost', 'Software Engineer', 'Edinburgh', 1667, '2008/12/13', '103,600'],
['Jena Gaines', 'Office Manager', 'London', 3814, '2008/12/19', '90,560'],
['Quinn Flynn', 'Support Lead', 'Edinburgh', 9497, '2013/03/03', '342,000'],
['Charde Marshall', 'Regional Director', 'San Francisco', 6741, '2008/10/16', '470,600'],
['Haley Kennedy', 'Senior Marketing Designer', 'London', 3597, '2012/12/18', '313,500'],
['Tatyana Fitzpatrick', 'Regional Director', 'London', 1965, '2010/03/17', '385,750'],
['Michael Silva', 'Marketing Designer', 'London', 1581, '2012/11/27', '198,500'],
['Paul Byrd', 'Chief Financial Officer (CFO)', 'New York', 3059, '2010/06/09', '725,000'],
['Gloria Little', 'Systems Administrator', 'New York', 1721, '2009/04/10', '237,500'],
['Bradley Greer', 'Software Engineer', 'London', 2558, '2012/10/13', '132,000'],
['Dai Rios', 'Personnel Lead', 'Edinburgh', 2290, '2012/09/26', '217,500'],
['Jenette Caldwell', 'Development Lead', 'New York', 1937, '2011/09/03', '345,000'],
['Yuri Berry', 'Chief Marketing Officer (CMO)', 'New York', 6154, '2009/06/25', '675,000'],
['Caesar Vance', 'Pre-Sales Support', 'New York', 8330, '2011/12/12', '106,450'],
['Doris Wilder', 'Sales Assistant', 'Sidney', 3023, '2010/09/20', '85,600'],
['Angelica Ramos', 'Chief Executive Officer (CEO)', 'London', 5797, '2009/10/09', '1,200,000'],
['Gavin Joyce', 'Developer', 'Edinburgh', 8822, '2010/12/22', '92,575'],
['Jennifer Chang', 'Regional Director', 'Singapore', 9239, '2010/11/14', '357,650'],
['Brenden Wagner', 'Software Engineer', 'San Francisco', 1314, '2011/06/07', '206,850'],
['Fiona Green', 'Chief Operating Officer (COO)', 'San Francisco', 2947, '2010/03/11', '850,000'],
['Shou Itou', 'Regional Marketing', 'Tokyo', 8899, '2011/08/14', '163,000'],
['Michelle House', 'Integration Specialist', 'Sidney', 2769, '2011/06/02', '95,400'],
['Suki Burks', 'Developer', 'London', 6832, '2009/10/22', '114,500'],
['Prescott Bartlett', 'Technical Author', 'London', 3606, '2011/05/07', '145,000'],
['Gavin Cortez', 'Team Leader', 'San Francisco', 2860, '2008/10/26', '235,500'],
['Martena Mccray', 'Post-Sales support', 'Edinburgh', 8240, '2011/03/09', '324,050'],
['Unity Butler', 'Marketing Designer', 'San Francisco', 5384, '2009/12/09', '85,675'],
['Howard Hatfield', 'Office Manager', 'San Francisco', 7031, '2008/12/16', '164,500'],
['Hope Fuentes', 'Secretary', 'San Francisco', 6318, '2010/02/12', '109,850'],
['Vivian Harrell', 'Financial Controller', 'San Francisco', 9422, '2009/02/14', '452,500'],
['Timothy Mooney', 'Office Manager', 'London', 7580, '2008/12/11', '136,200'],
['Jackson Bradshaw', 'Director', 'New York', 1042, '2008/09/26', '645,750'],
['Olivia Liang', 'Support Engineer', 'Singapore', 2120, '2011/02/03', '234,500'],
['Bruno Nash', 'Software Engineer', 'London', 6222, '2011/05/03', '163,500'],
['Sakura Yamamoto', 'Support Engineer', 'Tokyo', 9383, '2009/08/19', '139,575'],
['Thor Walton', 'Developer', 'New York', 8327, '2013/08/11', '98,540'],
['Finn Camacho', 'Support Engineer', 'San Francisco', 2927, '2009/07/07', '87,500'],
['Serge Baldwin', 'Data Coordinator', 'Singapore', 8352, '2012/04/09', '138,575'],
['Zenaida Frank', 'Software Engineer', 'New York', 7439, '2010/01/04', '125,250'],
['Zorita Serrano', 'Software Engineer', 'San Francisco', 4389, '2012/06/01', '115,000'],
['Jennifer Acosta', 'Junior Javascript Developer', 'Edinburgh', 3431, '2013/02/01', '75,650'],
['Cara Stevens', 'Sales Assistant', 'New York', 3990, '2011/12/06', '145,600'],
['Hermione Butler', 'Regional Director', 'London', 1016, '2011/03/21', '356,250'],
['Lael Greer', 'Systems Administrator', 'London', 6733, '2009/02/27', '103,500'],
['Jonas Alexander', 'Developer', 'San Francisco', 8196, '2010/07/14', '86,500'],
['Shad Decker', 'Regional Director', 'Edinburgh', 6373, '2008/11/13', '183,000'],
['Michael Bruce', 'Javascript Developer', 'Singapore', 5384, '2011/06/27', '183,000'],
['Donna Snider', 'Customer Support', 'New York', 4226, '2011/01/25', '112,000']
];
if (multiplier) {
Array.from(new Array(multiplier - 1)).forEach(d => {
data = data.concat(data);
});
}
return {
columns,
data
};
}
function getTreeData() {
return {
columns: [{
'id': 'account',
'content': 'Account'
}, {
'id': 'opening_debit',
'content': 'Opening (Dr)'
}, {
'id': 'opening_credit',
'content': 'Opening (Cr)'
},
// {
// 'id': 'debit',
// 'content': 'Debit'
// },
// {
// 'id': 'credit',
// 'content': 'Credit'
// },
{
'id': 'closing_debit',
'content': 'Closing (Dr)'
}, {
'id': 'closing_credit',
'content': 'Closing (Cr)'
}, {
'id': 'currency',
'content': 'Currency',
'hidden': 1
}],
data: [{
'account_name': 'Application of Funds (Assets)',
'account': 'Application of Funds (Assets)',
'parent_account': null,
'indent': 0,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 12023729.54,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 12023729.54,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Current Assets',
'account': 'Current Assets',
'parent_account': 'Application of Funds (Assets)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 13960649.54,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 13960649.54,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Accounts Receivable',
'account': 'Accounts Receivable',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 742790.474,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 742790.474,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Debtors',
'account': 'Debtors',
'parent_account': 'Accounts Receivable',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 742790.474,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 742790.474,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Bank Accounts',
'account': 'Bank Accounts',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 280676.822,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 280676.822,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Corporation Bank',
'account': 'Corporation Bank',
'parent_account': 'Bank Accounts',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 290676.822,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 290676.822,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'HDFC Bank',
'account': 'HDFC Bank',
'parent_account': 'Bank Accounts',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 10000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 10000.0,
'has_value': true
}, {
'account_name': 'Cash In Hand',
'account': 'Cash In Hand',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 229904.494,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 229904.494,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Cash',
'account': 'Cash',
'parent_account': 'Cash In Hand',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 229904.494,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 229904.494,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Stock Assets',
'account': 'Stock Assets',
'parent_account': 'Current Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 12707277.75,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 12707277.75,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'All Warehouses',
'account': 'All Warehouses',
'parent_account': 'Stock Assets',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 12707277.75,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 12707277.75,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Finished Goods',
'account': 'Finished Goods',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 87320.3,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 87320.3,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Retail Stores',
'account': 'Retail Stores',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 4540590.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 4540590.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Bandra Store',
'account': 'Bandra Store',
'parent_account': 'Retail Stores',
'indent': 5,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 3246800.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 3246800.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Central Warehouse',
'account': 'Central Warehouse',
'parent_account': 'Retail Stores',
'indent': 5,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 1236790.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 1236790.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Lower Parel Store',
'account': 'Lower Parel Store',
'parent_account': 'Retail Stores',
'indent': 5,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 57000.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 57000.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Stores',
'account': 'Stores',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 8016525.27,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 8016525.27,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Work In Progress',
'account': 'Work In Progress',
'parent_account': 'All Warehouses',
'indent': 4,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 62842.18,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 62842.18,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Fixed Assets',
'account': 'Fixed Assets',
'parent_account': 'Application of Funds (Assets)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 19920.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 19920.0,
'has_value': true
}, {
'account_name': 'Electronic Equipments',
'account': 'Electronic Equipments',
'parent_account': 'Fixed Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 80.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 80.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'Furnitures and Fixtures',
'account': 'Furnitures and Fixtures',
'parent_account': 'Fixed Assets',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 20000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 20000.0,
'has_value': true
}, {
'account_name': 'Temporary Accounts',
'account': 'Temporary Accounts',
'parent_account': 'Application of Funds (Assets)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1917000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1917000.0,
'has_value': true
}, {
'account_name': 'Temporary Opening',
'account': 'Temporary Opening',
'parent_account': 'Temporary Accounts',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1917000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1917000.0,
'has_value': true
}, {
'account_name': 'Source of Funds (Liabilities)',
'account': 'Source of Funds (Liabilities)',
'parent_account': null,
'indent': 0,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 2371628.002,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 2371628.002,
'has_value': true
}, {
'account_name': 'Current Liabilities',
'account': 'Current Liabilities',
'parent_account': 'Source of Funds (Liabilities)',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 2371628.002,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 2371628.002,
'has_value': true
}, {
'account_name': 'Accounts Payable',
'account': 'Accounts Payable',
'parent_account': 'Current Liabilities',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 368311.85,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 368311.85,
'has_value': true
}, {
'account_name': 'Creditors',
'account': 'Creditors',
'parent_account': 'Accounts Payable',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 194871.85,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 194871.85,
'has_value': true
}, {
'account_name': 'Salary Payable',
'account': 'Salary Payable',
'parent_account': 'Accounts Payable',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 173440.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 173440.0,
'has_value': true
}, {
'account_name': 'Duties and Taxes',
'account': 'Duties and Taxes',
'parent_account': 'Current Liabilities',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 150146.822,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 150146.822,
'has_value': true
}, {
'account_name': 'CGST',
'account': 'CGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 51479.591,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 51479.591,
'has_value': true
}, {
'account_name': 'IGST',
'account': 'IGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 1944.0,
'opening_credit': 0.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 1944.0,
'closing_credit': 0.0,
'has_value': true
}, {
'account_name': 'SGST',
'account': 'SGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 97711.231,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 97711.231,
'has_value': true
}, {
'account_name': 'UGST',
'account': 'UGST',
'parent_account': 'Duties and Taxes',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 2900.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 2900.0,
'has_value': true
}, {
'account_name': 'Stock Liabilities',
'account': 'Stock Liabilities',
'parent_account': 'Current Liabilities',
'indent': 2,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1853169.33,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1853169.33,
'has_value': true
}, {
'account_name': 'Stock Received But Not Billed',
'account': 'Stock Received But Not Billed',
'parent_account': 'Stock Liabilities',
'indent': 3,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 1853169.33,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 1853169.33,
'has_value': true
}, {
'account_name': 'Equity',
'account': 'Equity',
'parent_account': null,
'indent': 0,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 10000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 10000.0,
'has_value': true
}, {
'account_name': 'Capital Stock',
'account': 'Capital Stock',
'parent_account': 'Equity',
'indent': 1,
'from_date': '2018-04-01',
'to_date': '2019-03-31',
'currency': 'INR',
'opening_debit': 0.0,
'opening_credit': 10000.0,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 0.0,
'closing_credit': 10000.0,
'has_value': true
}, {}, {
'account': 'Total',
'account_name': 'Total',
'warn_if_negative': true,
'opening_debit': 32260956.43,
'opening_credit': 22618854.891999997,
'debit': 0.0,
'credit': 0.0,
'closing_debit': 32260956.43,
'closing_credit': 22618854.891999997,
'parent_account': null,
'indent': 0,
'has_value': true,
'currency': 'INR'
}]
};
}

View File

@ -1,172 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Frappe DataTable - A simple, modern datatable library for the web</title>
<link href="assets/css/frappe-datatable.css" rel="stylesheet">
<link href="assets/css/hljs-default.css" rel="stylesheet">
<link href="assets/css/index.css" rel="stylesheet">
</head>
<body>
<nav class="navbar">
<div class="container">
<h3>Frappe DataTable</h3>
<div class="navbar-links">
<a href="https://github.com/frappe/datatable">GitHub</a>
</div>
</div>
</nav>
<div class="container">
<section class="showcase align-center">
<img class="logo" src="assets/img/data-table-logo.svg" />
<h1 class="text-center">A simple, modern and interactive<br> datatable for the web</h1>
<div class="example-1"></div>
</section>
<section class="showcase installation">
<h3>Installation</h3>
<div class="code">
<pre><code class="shell"># Install using yarn
$ yarn add frappe-datatable
# or npm
$ npm install frappe-datatable</code></pre>
</section>
<section class="showcase installation">
<h3>Usage</h3>
<div class="code">
<pre><code>import DataTable from 'frappe-datatable';
// or add
// &lt;script src="frappe-datatable.js" &gt;&lt;/script&gt;
// in your html
let datatable = new DataTable({
columns: ['Name', 'Position', ...],
data: [
['Tiger Nixon', 'System Architect', ...],
['Garrett Winters', 'Accountant', ...],
...
]
});</code></pre>
</section>
<section class="showcase feature">
<h3>Cell Features</h3>
<div class="features">
<ul>
<li>Custom Formatters</li>
<li>Inline Editing</li>
<li>Mouse Selection</li>
</ul>
<ul>
<li>Copy Cells</li>
<li>Keyboard Navigation</li>
<li>Custom Cell Editor</li>
</ul>
</div>
</section>
<section class="showcase feature">
<h3>Column Features</h3>
<div class="features">
<ul>
<li>Reorder Columns</li>
<li>Sort by Column</li>
<li>Remove / Hide Column</li>
</ul>
<ul>
<li>Custom Actions</li>
<li>Resize Column</li>
<li>Flexible Layout</li>
</ul>
</div>
</section>
<section class="showcase feature">
<h3>Row Features</h3>
<div class="features">
<ul>
<li>Row Selection</li>
<li>Tree Structured Rows</li>
<li>Inline Filters</li>
</ul>
<ul>
<li>Large Number of Rows</li>
<li>Dynamic Row Height</li>
</ul>
</div>
</section>
<section class="showcase installation">
<h3>List of configurable options</h3>
<div class="code">
<pre><code>{
columns: [],
data: [],
dropdownButton: '▼',
headerDropdown: [
{
label: 'Custom Action',
action: console.log
}
],
events: {
onRemoveColumn(column) {
},
onSwitchColumn(column1, column2) {
},
onSortColumn(column) {
}
},
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
},
freezeMessage: '',
getEditor: () => {
},
addSerialNoColumn: true,
addCheckboxColumn: false,
enableClusterize: true,
enableLogs: false,
layout: 'fixed', // fixed, fluid
noDataMessage: 'No Data',
cellHeight: null,
enableInlineFilters: false
}</code></pre>
</div>
</section>
<section class="showcase align-center">
<p>
<button class="download-button">Download</button>
</p>
<p>
<a href="https://github.com/frappe/datatable">View on GitHub</a>
</p>
<p>
<span>MIT License</span>
</p>
</section>
</div>
<footer class="showcase align-center">
<img class="footer-logo" src="assets/img/frappe-bird-grey.svg" alt="Frappe Logo">
<section>Made with ❤️ by <a>Frappe</a></section>
</footer>
<script src="assets/js/clusterize.min.js"></script>
<script src="assets/js/Sortable.min.js"></script>
<script src="assets/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="assets/js/frappe-datatable.js"></script>
<script src="assets/js/index.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,31 +1,53 @@
{
"name": "frappe-datatable",
"version": "0.0.5",
"version": "0.0.0-development",
"description": "A modern datatable library for the web",
"main": "dist/frappe-datatable.cjs.js",
"unpkg": "dist/frappe-datatable.min.js",
"jsdelivr": "dist/frappe-datatable.min.js",
"scripts": {
"start": "yarn run dev",
"build": "rollup -c",
"production": "rollup -c --production",
"build:docs": "rollup -c --docs",
"build": "rollup -c && NODE_ENV=production rollup -c",
"dev": "rollup -c -w",
"test": "mocha --compilers js:babel-core/register --colors ./test/*.spec.js"
"cy:server": "http-server -p 8989",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test": "start-server-and-test cy:server http://localhost:8989 cy:run",
"test-local": "start-server-and-test cy:server http://localhost:8989 cy:open",
"travis-deploy-once": "travis-deploy-once",
"semantic-release": "semantic-release",
"lint": "eslint src",
"lint-and-build": "yarn lint && yarn build",
"commit": "npx git-cz"
},
"files": [
"dist",
"src"
],
"devDependencies": {
"autoprefixer": "^9.0.0",
"chai": "3.5.0",
"cypress": "^9.2.0",
"cz-conventional-changelog": "^2.1.0",
"deepmerge": "^2.0.1",
"eslint": "^5.0.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.11.0",
"http-server": "^0.11.1",
"mocha": "3.3.0",
"postcss-cssnext": "^3.1.0",
"postcss-custom-properties": "^7.0.0",
"postcss-nested": "^3.0.0",
"rollup": "^0.59.4",
"rollup-plugin-commonjs": "^8.3.0",
"rollup-plugin-eslint": "^4.0.0",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-resolve": "^3.0.3",
"rollup-plugin-postcss": "^1.2.8",
"rollup-plugin-uglify-es": "^0.0.1"
"rollup-plugin-uglify-es": "^0.0.1",
"semantic-release": "^17.1.1",
"start-server-and-test": "^1.4.1",
"travis-deploy-once": "^5.0.1"
},
"repository": {
"type": "git",
@ -42,10 +64,15 @@
"bugs": {
"url": "https://github.com/frappe/datatable/issues"
},
"homepage": "https://frappe.github.io/datatable",
"homepage": "https://frappe.io/datatable",
"dependencies": {
"clusterize.js": "^0.18.0",
"hyperlist": "^1.0.0-beta",
"lodash": "^4.17.5",
"sortablejs": "^1.7.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
}
}

View File

@ -4,11 +4,12 @@ import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import nested from 'postcss-nested';
import cssnext from 'postcss-cssnext';
import customProperties from 'postcss-custom-properties';
import autoprefixer from 'autoprefixer';
import eslint from 'rollup-plugin-eslint';
import merge from 'deepmerge';
const production = process.argv[3] === '--production';
const production = process.env.NODE_ENV === 'production';
const baseJS = {
input: 'src/index.js',
@ -40,8 +41,9 @@ const baseCSS = {
extract: true,
minimize: production,
plugins: [
customProperties(),
nested(),
cssnext()
autoprefixer()
]
})
]
@ -85,24 +87,8 @@ const prodCSS = merge(devCSS, {
}
});
// docs
const docJS = merge(devIIFE, {
output: {
file: 'docs/assets/js/frappe-datatable.js'
}
});
const docCSS = merge(devCSS, {
output: {
file: 'docs/assets/css/frappe-datatable.css'
}
});
const developmentAssets = [devIIFE, devCjs, devCSS];
const documentationAssets = [docJS, docCSS];
const productionAssets = [prodIIFE, prodCSS];
const docs = process.argv[3] === '--docs';
const assets = docs ? documentationAssets : production ? productionAssets : developmentAssets;
const assets = production ? productionAssets : developmentAssets;
export default assets;

View File

@ -1,6 +1,4 @@
import Clusterize from 'clusterize.js';
import $ from './dom';
import { nextTick } from './utils';
import HyperList from 'hyperlist';
export default class BodyRenderer {
constructor(instance) {
@ -10,60 +8,115 @@ export default class BodyRenderer {
this.rowmanager = instance.rowmanager;
this.cellmanager = instance.cellmanager;
this.bodyScrollable = instance.bodyScrollable;
this.footer = this.instance.footer;
this.log = instance.log;
this.appendRemainingData = nextTick(this.appendRemainingData, this);
}
renderRows(rows) {
this.visibleRows = rows;
this.visibleRowIndices = rows.map(row => row.meta.rowIndex);
if (rows.length === 0) {
this.bodyScrollable.innerHTML = this.getNoDataHTML();
this.footer.innerHTML = '';
return;
}
// Create a temporary set for faster lookups.
// We can't change this.visibleRowIndices as it would be breaking for users.
let visibleRowIndicesSet = new Set(this.visibleRowIndices);
const rowViewOrder = this.datamanager.rowViewOrder.map(index => {
if (visibleRowIndicesSet.has(index)) {
return index;
}
return null;
}).filter(index => index !== null);
const computedStyle = getComputedStyle(this.bodyScrollable);
let config = {
width: computedStyle.width,
height: computedStyle.height,
itemHeight: this.options.cellHeight,
total: rows.length,
generate: (index) => {
const el = document.createElement('div');
const rowIndex = rowViewOrder[index];
const row = this.datamanager.getRow(rowIndex);
const rowHTML = this.rowmanager.getRowHTML(row, row.meta);
el.innerHTML = rowHTML;
return el.children[0];
},
afterRender: () => {
this.restoreState();
}
};
if (!this.hyperlist) {
this.hyperlist = new HyperList(this.bodyScrollable, config);
} else {
this.hyperlist.refresh(this.bodyScrollable, config);
}
this.renderFooter();
}
render() {
if (this.options.clusterize) {
this.renderBodyWithClusterize();
} else {
this.renderBodyHTML();
}
}
renderBodyHTML() {
const rows = this.datamanager.getRowsForView();
this.bodyScrollable.innerHTML = this.getBodyHTML(rows);
this.renderRows(rows);
// setDimensions requires atleast 1 row to exist in dom
this.instance.setDimensions();
this.restoreState();
}
renderBodyWithClusterize() {
// first page
const rows = this.datamanager.getRowsForView(0, 20);
let initialData = this.getDataForClusterize(rows);
renderFooter() {
if (!this.options.showTotalRow) return;
if (initialData.length === 0) {
initialData = [this.getNoDataHTML()];
}
const totalRow = this.getTotalRow();
let html = this.rowmanager.getRowHTML(totalRow, { isTotalRow: 1, rowIndex: 'totalRow' });
if (!this.clusterize) {
// empty body
this.bodyScrollable.innerHTML = this.getBodyHTML([]);
this.footer.innerHTML = html;
}
// first 20 rows will appended
// rest of them in nextTick
this.clusterize = new Clusterize({
rows: initialData,
scrollElem: this.bodyScrollable,
contentElem: $('tbody', this.bodyScrollable),
callbacks: {
clusterChanged: () => this.restoreState()
},
/* eslint-disable */
show_no_data_row: false,
/* eslint-enable */
});
getTotalRow() {
const columns = this.datamanager.getColumns();
const totalRowTemplate = columns.map(col => {
let content = null;
if (['_rowIndex', '_checkbox'].includes(col.id)) {
content = '';
}
return {
content,
isTotalRow: 1,
colIndex: col.colIndex,
column: col
};
});
// setDimensions requires atleast 1 row to exist in dom
this.instance.setDimensions();
} else {
this.clusterize.update(initialData);
}
const totalRow = totalRowTemplate.map((cell, i) => {
if (cell.content === '') return cell;
this.appendRemainingData();
if (this.options.hooks.columnTotal) {
const columnValues = this.visibleRows.map(row => row[i].content);
const result = this.options.hooks.columnTotal.call(this.instance, columnValues, cell);
if (result != null) {
cell.content = result;
return cell;
}
}
cell.content = this.visibleRows.reduce((acc, prevRow) => {
const prevCell = prevRow[i];
if (typeof prevCell.content === 'number') {
if (acc == null) acc = 0;
return acc + prevCell.content;
}
return acc;
}, cell.content);
return cell;
});
return totalRow;
}
restoreState() {
@ -72,12 +125,6 @@ export default class BodyRenderer {
this.cellmanager.focusCellOnClusterChanged();
}
appendRemainingData() {
const rows = this.datamanager.getRowsForView(20);
const data = this.getDataForClusterize(rows);
this.clusterize.append(data);
}
showToastMessage(message, hideAfter) {
this.instance.toastMessage.innerHTML = this.getToastMessageHTML(message);
@ -92,20 +139,6 @@ export default class BodyRenderer {
this.instance.toastMessage.innerHTML = '';
}
getDataForClusterize(rows) {
return rows.map(row => this.rowmanager.getRowHTML(row, row.meta));
}
getBodyHTML(rows) {
return `
<table class="dt-body">
<tbody>
${rows.map(row => this.rowmanager.getRowHTML(row, row.meta)).join('')}
</tbody>
</table>
`;
}
getNoDataHTML() {
return `<div class="dt-scrollable__no-data">${this.options.noDataMessage}</div>`;
}

View File

@ -2,9 +2,11 @@ import {
copyTextToClipboard,
makeDataAttributeString,
throttle,
linkProperties
linkProperties,
escapeHTML,
} from './utils';
import $ from './dom';
import icons from './icons';
export default class CellManager {
constructor(instance) {
@ -13,14 +15,18 @@ export default class CellManager {
'wrapper',
'options',
'style',
'header',
'bodyScrollable',
'columnmanager',
'rowmanager',
'datamanager',
'keyboard'
'keyboard',
'footer'
]);
this.bindEvents();
this.stickyRowWidth = 0;
this.stickyColWitdh = [];
}
bindEvents() {
@ -49,34 +55,12 @@ export default class CellManager {
this.activateEditing(this.$focusedCell);
} else if (this.$editingCell) {
// enter keypress on editing cell
this.submitEditing();
this.deactivateEditing();
}
});
}
bindKeyboardNav() {
const focusCell = (direction) => {
if (!this.$focusedCell || this.$editingCell) {
return false;
}
let $cell = this.$focusedCell;
if (direction === 'left' || direction === 'shift+tab') {
$cell = this.getLeftCell$($cell);
} else if (direction === 'right' || direction === 'tab') {
$cell = this.getRightCell$($cell);
} else if (direction === 'up') {
$cell = this.getAboveCell$($cell);
} else if (direction === 'down') {
$cell = this.getBelowCell$($cell);
}
this.focusCell($cell);
return true;
};
const focusLastCell = (direction) => {
if (!this.$focusedCell || this.$editingCell) {
return false;
@ -103,13 +87,14 @@ export default class CellManager {
};
['left', 'right', 'up', 'down', 'tab', 'shift+tab']
.map(direction => this.keyboard.on(direction, () => focusCell(direction)));
.map(direction => this.keyboard.on(direction, () => this.focusCellInDirection(direction)));
['left', 'right', 'up', 'down']
.map(direction => this.keyboard.on(`ctrl+${direction}`, () => focusLastCell(direction)));
this.keyboard.on('esc', () => {
this.deactivateEditing();
this.deactivateEditing(false);
this.columnmanager.toggleFilter(false);
});
if (this.options.inlineFilters) {
@ -120,6 +105,10 @@ export default class CellManager {
this.activateFilter(colIndex);
return true;
});
$.on(this.header, 'focusin', '.dt-filter', () => {
this.unfocusCell(this.$focusedCell);
});
}
}
@ -148,7 +137,10 @@ export default class CellManager {
bindCopyCellContents() {
this.keyboard.on('ctrl+c', () => {
const noOfCellsCopied = this.copyCellContents(this.$focusedCell, this.$selectionCursor);
const message = `${noOfCellsCopied} cell${noOfCellsCopied > 1 ? 's' : ''} copied`;
const message = this.instance.translate('{count} cells copied', {
count: noOfCellsCopied
});
if (noOfCellsCopied) {
this.instance.showToastMessage(message, 2);
}
@ -183,6 +175,14 @@ export default class CellManager {
mouseDown = false;
});
if (this.options.showTotalRow) {
$.on(this.footer, 'click', '.dt-cell', (e) => {
this.focusCell($(e.delegatedTarget));
});
}
const selectArea = (e) => {
if (!mouseDown) return;
this.selectArea($(e.delegatedTarget));
@ -205,7 +205,9 @@ export default class CellManager {
}
focusCell($cell, {
skipClearSelection = 0
skipClearSelection = 0,
skipDOMFocus = 0,
skipScrollToCell = 0
} = {}) {
if (!$cell) return;
@ -225,7 +227,9 @@ export default class CellManager {
return;
}
this.scrollToCell($cell);
if (!skipScrollToCell) {
this.scrollToCell($cell);
}
this.deactivateEditing();
if (!skipClearSelection) {
@ -239,12 +243,27 @@ export default class CellManager {
this.$focusedCell = $cell;
$cell.classList.add('dt-cell--focus');
// so that keyboard nav works
$cell.focus();
if (!skipDOMFocus) {
// so that keyboard nav works
$cell.focus();
}
this.highlightRowColumnHeader($cell);
}
unfocusCell($cell) {
if (!$cell) return;
// remove cell border
$cell.classList.remove('dt-cell--focus');
this.$focusedCell = null;
// reset header background
if (this.lastHeaders) {
this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
}
}
highlightRowColumnHeader($cell) {
const {
colIndex,
@ -256,14 +275,14 @@ export default class CellManager {
const rowHeaderSelector = `.dt-cell--${srNoColIndex}-${rowIndex}`;
if (this.lastHeaders) {
this.lastHeaders.forEach(header => header.classList.remove('dt-cell--highlight'));
this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
}
const colHeader = $(colHeaderSelector, this.wrapper);
const rowHeader = $(rowHeaderSelector, this.wrapper);
this.lastHeaders = [colHeader, rowHeader];
this.lastHeaders.forEach(header => header.classList.add('dt-cell--highlight'));
this.lastHeaders.forEach(header => header && header.classList.add('dt-cell--highlight'));
}
selectAreaOnClusterChanged() {
@ -293,11 +312,15 @@ export default class CellManager {
const $cell = this.getCell$(colIndex, rowIndex);
if (!$cell) return;
// this function is called after selectAreaOnClusterChanged,
// this function is called after hyperlist renders the rows after scroll,
// focusCell calls clearSelection which resets the area selection
// so a flag to skip it
// we also skip DOM focus and scroll to cell
// because it fights with the user scroll
this.focusCell($cell, {
skipClearSelection: 1
skipClearSelection: 1,
skipDOMFocus: 1,
skipScrollToCell: 1
});
}
@ -433,7 +456,10 @@ export default class CellManager {
}
}
deactivateEditing() {
deactivateEditing(submitValue = true) {
if (submitValue) {
this.submitEditing();
}
// keep focus on the cell so that keyboard navigation works
if (this.$focusedCell) this.$focusedCell.focus();
@ -484,7 +510,9 @@ export default class CellManager {
}
submitEditing() {
if (!this.$editingCell) return;
let promise = Promise.resolve();
if (!this.$editingCell) return promise;
const $cell = this.$editingCell;
const {
rowIndex,
@ -496,25 +524,38 @@ export default class CellManager {
const editor = this.currentCellEditor;
if (editor) {
const value = editor.getValue();
const done = editor.setValue(value, rowIndex, col);
const oldValue = this.getCell(colIndex, rowIndex).content;
let valuePromise = editor.getValue();
// update cell immediately
this.updateCell(colIndex, rowIndex, value);
$cell.focus();
if (done && done.then) {
// revert to oldValue if promise fails
done.catch((e) => {
console.log(e);
this.updateCell(colIndex, rowIndex, oldValue);
});
// convert to stubbed Promise
if (!valuePromise.then) {
valuePromise = Promise.resolve(valuePromise);
}
promise = valuePromise.then((value) => {
const oldValue = this.getCell(colIndex, rowIndex).content;
if (oldValue === value) return false;
const done = editor.setValue(value, rowIndex, col);
// update cell immediately
this.updateCell(colIndex, rowIndex, value, true);
$cell.focus();
if (done && done.then) {
// revert to oldValue if promise fails
done.catch((e) => {
console.log(e);
this.updateCell(colIndex, rowIndex, oldValue);
});
}
return done;
});
}
}
this.currentCellEditor = null;
return promise;
}
copyCellContents($cell1, $cell2) {
@ -522,10 +563,18 @@ export default class CellManager {
// copy only focusedCell
const {
colIndex,
rowIndex
rowIndex,
isTotalRow
} = $.data($cell1);
const cell = this.getCell(colIndex, rowIndex);
copyTextToClipboard(cell.content);
let copiedContent = '';
if (isTotalRow) {
let choosenFooterCell = this.$focusedCell;
copiedContent = choosenFooterCell.children[0].title;
} else {
const cell = this.getCell(colIndex, rowIndex);
copiedContent = cell.content;
}
copyTextToClipboard(copiedContent);
return 1;
}
const cells = this.getCellsInRange($cell1, $cell2);
@ -576,7 +625,7 @@ export default class CellManager {
let rowIndex = i + focusedCell.rowIndex;
row.forEach((cell, j) => {
let colIndex = j + focusedCell.colIndex;
this.updateCell(colIndex, rowIndex, cell);
this.updateCell(colIndex, rowIndex, cell, true);
});
});
}
@ -587,20 +636,20 @@ export default class CellManager {
if (!this.columnmanager.isFilterShown) {
// put focus back on cell
this.$focusedCell.focus();
this.$focusedCell && this.$focusedCell.focus();
}
}
updateCell(colIndex, rowIndex, value) {
updateCell(colIndex, rowIndex, value, refreshHtml = false) {
const cell = this.datamanager.updateCell(colIndex, rowIndex, {
content: value
});
this.refreshCell(cell);
this.refreshCell(cell, refreshHtml);
}
refreshCell(cell) {
refreshCell(cell, refreshHtml = false) {
const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyScrollable);
$cell.innerHTML = this.getCellContent(cell);
$cell.innerHTML = this.getCellContent(cell, refreshHtml);
}
toggleTreeButton(rowIndex, flag) {
@ -616,6 +665,49 @@ export default class CellManager {
return colIndex < this.columnmanager.getFirstColumnIndex();
}
focusCellInDirection(direction) {
if (!this.$focusedCell || (this.$editingCell && ['left', 'right', 'up', 'down'].includes(direction))) {
return false;
} else if (this.$editingCell && ['tab', 'shift+tab'].includes(direction)) {
this.deactivateEditing();
}
let $cell = this.$focusedCell;
if (direction === 'left' || direction === 'shift+tab') {
$cell = this.getLeftCell$($cell);
} else if (direction === 'right' || direction === 'tab') {
$cell = this.getRightCell$($cell);
} else if (direction === 'up') {
$cell = this.getAboveCell$($cell);
} else if (direction === 'down') {
$cell = this.getBelowCell$($cell);
}
if (!$cell) {
return false;
}
const {
colIndex
} = $.data($cell);
const column = this.columnmanager.getColumn(colIndex);
if (!column.focusable) {
let $prevFocusedCell = this.$focusedCell;
this.unfocusCell($prevFocusedCell);
this.$focusedCell = $cell;
let ret = this.focusCellInDirection(direction);
if (!ret) {
this.focusCell($prevFocusedCell);
}
return ret;
}
this.focusCell($cell);
return true;
}
getCell$(colIndex, rowIndex) {
return $(this.selector(colIndex, rowIndex), this.bodyScrollable);
}
@ -681,7 +773,7 @@ export default class CellManager {
}
scrollToCell($cell) {
if ($.inViewport($cell, this.bodyScrollable)) return false;
if ($.inViewport($cell, this.bodyScrollable) || $.inViewport($cell, this.footer)) return false;
const {
rowIndex
@ -699,16 +791,54 @@ export default class CellManager {
rowIndex,
colIndex,
isHeader,
isFilter
isFilter,
isTotalRow
} = cell;
const dataAttr = makeDataAttributeString({
rowIndex,
colIndex,
isHeader,
isFilter
isFilter,
isTotalRow
});
const isBodyCell = !(isHeader || isFilter);
let styles = '';
const row = this.datamanager.getRow(rowIndex);
const isBodyCell = !(isHeader || isFilter || isTotalRow);
const serialNoColIndex = !this.options.checkboxColumn && this.options.serialNoColumn ? 0 : 1;
let sticky = false;
let checkboxserialNoclass = '';
if (colIndex === 0 && this.options.checkboxColumn) {
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) this.stickyRowWidth = 34;
checkboxserialNoclass = 'dt-cell-checkbox';
sticky = true;
} else if (colIndex === serialNoColIndex && this.options.serialNoColumn) {
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) {
this.stickyColWitdh[cell.id] = this.stickyRowWidth;
this.stickyRowWidth += (cell.width || 37);
checkboxserialNoclass = 'dt-cell-serial-no';
}
styles = `left:${this.stickyColWitdh[isBodyCell ? cell.column.id : cell.id]}px;`;
sticky = true;
} else if (cell.sticky) {
if (cell.isHeader && !(cell.id in this.stickyColWitdh)) {
this.stickyColWitdh[cell.id] = this.stickyRowWidth;
this.stickyRowWidth += ((cell.width || 100) + 1);
}
styles = `left:${this.stickyColWitdh[cell.id]}px;`;
sticky = true;
} else if ((isBodyCell || isTotalRow) && cell.column.sticky) {
styles = `left:${this.stickyColWitdh[cell.column.id]}px;`;
sticky = true;
}
const className = [
'dt-cell',
@ -717,17 +847,20 @@ export default class CellManager {
isBodyCell ? 'dt-cell--row-' + rowIndex : '',
isHeader ? 'dt-cell--header' : '',
isHeader ? `dt-cell--header-${colIndex}` : '',
isFilter ? 'dt-cell--filter' : ''
isFilter ? 'dt-cell--filter' : '',
isBodyCell && (row && row.meta.isTreeNodeClose) ? 'dt-cell--tree-close' : '',
sticky ? 'dt-sticky-col' : '',
checkboxserialNoclass,
].join(' ');
return `
<td class="${className}" ${dataAttr} tabindex="0">
<div class="${className}" ${dataAttr} tabindex="0" style="${styles}">
${this.getCellContent(cell)}
</td>
</div>
`;
}
getCellContent(cell) {
getCellContent(cell, refreshHtml = false) {
const {
isHeader,
isFilter,
@ -738,7 +871,11 @@ export default class CellManager {
const editCellHTML = editable ? this.getEditCellHTML(colIndex) : '';
const sortable = isHeader && cell.sortable !== false;
const sortIndicator = sortable ? '<span class="sort-indicator"></span>' : '';
const sortIndicator = sortable ?
`<span class="sort-indicator">
${this.options.sortIndicator[cell.sortOrder]}
</span>` :
'';
const resizable = isHeader && cell.resizable !== false;
const resizeColumn = resizable ? '<span class="dt-cell__resize-handle"></span>' : '';
@ -746,29 +883,41 @@ export default class CellManager {
const hasDropdown = isHeader && cell.dropdown !== false;
const dropdown = hasDropdown ? this.columnmanager.getDropdownHTML() : '';
const customFormatter = cell.format || (cell.column && cell.column.format) || null;
let customFormatter = CellManager.getCustomCellFormatter(cell);
let contentHTML;
if (isHeader || isFilter || !customFormatter) {
contentHTML = cell.content;
} else {
const row = this.datamanager.getRow(cell.rowIndex);
const data = this.datamanager.getData(cell.rowIndex);
contentHTML = customFormatter(cell.content, row, cell.column, data);
if (!cell.html || refreshHtml) {
const row = this.datamanager.getRow(cell.rowIndex);
const data = this.datamanager.getData(cell.rowIndex);
contentHTML = customFormatter(cell.content, row, cell.column, data);
} else {
contentHTML = cell.html;
}
}
cell.html = contentHTML;
if (this.options.treeView && !(isHeader || isFilter) && cell.indent !== undefined) {
const nextRow = this.datamanager.getRow(cell.rowIndex + 1);
const addToggle = nextRow && nextRow.meta.indent > cell.indent;
const leftPadding = 20;
const unit = 'px';
// Add toggle and indent in the first column
const firstColumnIndex = this.datamanager.getColumnIndexById('_rowIndex') + 1;
if (firstColumnIndex === cell.colIndex) {
const padding = ((cell.indent || 0) + 1) * 1.5;
const padding = ((cell.indent || 0)) * leftPadding;
const toggleHTML = addToggle ?
`<span class="dt-tree-node__toggle" style="left: ${padding - 1.5}rem"></span>` : '';
contentHTML = `<span class="dt-tree-node" style="padding-left: ${padding}rem">
${toggleHTML}${contentHTML}</span>`;
`<span class="dt-tree-node__toggle" style="left: ${padding - leftPadding}${unit}">
<span class="icon-open">${icons.chevronDown}</span>
<span class="icon-close">${icons.chevronRight}</span>
</span>` : '';
contentHTML = `<span class="dt-tree-node" style="padding-left: ${padding}${unit}">
${toggleHTML}
<span>${contentHTML}</span>
</span>`;
}
}
@ -777,7 +926,7 @@ export default class CellManager {
isHeader ? `dt-cell__content--header-${colIndex}` : `dt-cell__content--col-${colIndex}`
].join(' ');
return `
let cellContentHTML = `
<div class="${className}">
${contentHTML}
${sortIndicator}
@ -786,6 +935,16 @@ export default class CellManager {
</div>
${editCellHTML}
`;
let div = document.createElement('div');
div.innerHTML = contentHTML;
let textContent = div.textContent;
textContent = textContent.replace(/\s+/g, ' ').trim();
cellContentHTML = cellContentHTML.replace('>', ` title="${escapeHTML(textContent)}">`);
return cellContentHTML;
}
getEditCellHTML(colIndex) {
@ -795,4 +954,8 @@ export default class CellManager {
selector(colIndex, rowIndex) {
return `.dt-cell--${colIndex}-${rowIndex}`;
}
static getCustomCellFormatter(cell) {
return cell.format || (cell.column && cell.column.format) || null;
}
}

View File

@ -14,57 +14,35 @@ export default class ColumnManager {
'fireEvent',
'header',
'datamanager',
'cellmanager',
'style',
'wrapper',
'rowmanager',
'bodyScrollable'
'bodyScrollable',
'bodyRenderer'
]);
this.bindEvents();
}
renderHeader() {
this.header.innerHTML = '<thead></thead>';
this.header.innerHTML = '<div></div>';
this.refreshHeader();
}
refreshHeader() {
const columns = this.datamanager.getColumns();
const $cols = $.each('.dt-cell--header', this.header);
const refreshHTML =
// first init
!$('.dt-cell', this.header) ||
// deleted column
columns.length < $cols.length;
// refresh html
$('div', this.header).innerHTML = this.getHeaderHTML(columns);
if (refreshHTML) {
// refresh html
$('thead', this.header).innerHTML = this.getHeaderHTML(columns);
this.$filterRow = $('.dt-row[data-is-filter]', this.header);
if (this.$filterRow) {
$.style(this.$filterRow, { display: 'none' });
}
} else {
// update data-attributes
$cols.map(($col, i) => {
const column = columns[i];
// column sorted or order changed
// update colIndex of each header cell
$.data($col, {
colIndex: column.colIndex
});
// refresh sort indicator
const sortIndicator = $('.sort-indicator', $col);
if (sortIndicator) {
sortIndicator.innerHTML = this.options.sortIndicator[column.sortOrder];
}
});
this.$filterRow = $('.dt-row-filter', this.header);
if (this.$filterRow) {
$.style(this.$filterRow, { display: 'none' });
}
// reset columnMap
this.$columnMap = [];
this.bindMoveColumn();
}
getHeaderHTML(columns) {
@ -82,51 +60,77 @@ export default class ColumnManager {
bindEvents() {
this.bindDropdown();
this.bindResizeColumn();
this.bindMoveColumn();
this.bindPerfectColumnWidth();
this.bindFilter();
}
bindDropdown() {
let $activeDropdown;
let activeClass = 'dt-dropdown--active';
let toggleClass = '.dt-dropdown__toggle';
let dropdownClass = '.dt-dropdown__list';
$.on(this.header, 'click', toggleClass, (e, $button) => {
const $dropdown = $.closest('.dt-dropdown', $button);
// attach the dropdown list to container
this.instance.dropdownContainer.innerHTML = this.getDropdownListHTML();
this.$dropdownList = this.instance.dropdownContainer.firstElementChild;
if (!$dropdown.classList.contains(activeClass)) {
deactivateDropdown();
$dropdown.classList.add(activeClass);
$activeDropdown = $dropdown;
} else {
deactivateDropdown();
}
$.on(this.header, 'click', toggleClass, e => {
this.openDropdown(e);
});
$.on(document.body, 'click', (e) => {
if (e.target.matches(toggleClass)) return;
const deactivateDropdownOnBodyClick = (e) => {
const selector = [
toggleClass, toggleClass + ' *',
dropdownClass, dropdownClass + ' *'
].join(',');
if (e.target.matches(selector)) return;
deactivateDropdown();
};
$.on(document.body, 'click', deactivateDropdownOnBodyClick);
document.addEventListener('scroll', deactivateDropdown, true);
this.instance.on('onDestroy', () => {
$.off(document.body, 'click', deactivateDropdownOnBodyClick);
$.off(document, 'scroll', deactivateDropdown);
});
const dropdownItems = this.options.headerDropdown;
$.on(this.header, 'click', '.dt-dropdown__list-item', (e, $item) => {
const $col = $.closest('.dt-cell', $item);
const {
index
} = $.data($item);
const {
colIndex
} = $.data($col);
$.on(this.$dropdownList, 'click', '.dt-dropdown__list-item', (e, $item) => {
if (!this._dropdownActiveColIndex) return;
const dropdownItems = this.options.headerDropdown;
const { index } = $.data($item);
const colIndex = this._dropdownActiveColIndex;
let callback = dropdownItems[index].action;
callback && callback.call(this.instance, this.getColumn(colIndex));
this.hideDropdown();
});
const _this = this;
function deactivateDropdown(e) {
$activeDropdown && $activeDropdown.classList.remove(activeClass);
$activeDropdown = null;
_this.hideDropdown();
}
this.hideDropdown();
}
openDropdown(e) {
if (!this._dropdownWidth) {
$.style(this.$dropdownList, { display: '' });
this._dropdownWidth = $.style(this.$dropdownList, 'width');
}
$.style(this.$dropdownList, {
display: '',
left: (e.clientX - this._dropdownWidth + 4) + 'px',
top: (e.clientY + 4) + 'px'
});
const $cell = $.closest('.dt-cell', e.target);
const { colIndex } = $.data($cell);
this._dropdownActiveColIndex = colIndex;
}
hideDropdown() {
$.style(this.$dropdownList, {
display: 'none'
});
this._dropdownActiveColIndex = null;
}
bindResizeColumn() {
@ -151,7 +155,7 @@ export default class ColumnManager {
startX = e.pageX;
});
$.on(document.body, 'mouseup', (e) => {
const onMouseup = (e) => {
document.body.classList.remove('dt-resize');
if (!$resizingCell) return;
isDragging = false;
@ -162,61 +166,95 @@ export default class ColumnManager {
this.setColumnWidth(colIndex);
this.style.setBodyStyle();
$resizingCell = null;
};
$.on(document.body, 'mouseup', onMouseup);
this.instance.on('onDestroy', () => {
$.off(document.body, 'mouseup', onMouseup);
});
$.on(document.body, 'mousemove', (e) => {
const onMouseMove = (e) => {
if (!isDragging) return;
const finalWidth = startWidth + (e.pageX - startX);
let delta = e.pageX - startX;
if (this.options.direction === 'rtl') {
delta = -1 * delta;
}
const finalWidth = startWidth + delta;
const {
colIndex
} = $.data($resizingCell);
if (this.getColumnMinWidth(colIndex) > finalWidth) {
// don't resize past minWidth
let columnMinWidth = this.options.minimumColumnWidth;
if (columnMinWidth > finalWidth) {
// don't resize past 30 pixels
return;
}
this.datamanager.updateColumn(colIndex, {
width: finalWidth
});
this.setColumnHeaderWidth(colIndex);
};
$.on(document.body, 'mousemove', onMouseMove);
this.instance.on('onDestroy', () => {
$.off(document.body, 'mousemove', onMouseMove);
});
}
bindPerfectColumnWidth() {
$.on(this.header, 'dblclick', '.dt-cell .dt-cell__resize-handle', (e, $handle) => {
const $cell = $handle.parentNode.parentNode;
const { colIndex } = $.data($cell);
let longestCell = this.bodyRenderer.visibleRows
.map(d => d[colIndex])
.reduce((acc, curr) => acc.content.length > curr.content.length ? acc : curr);
let $longestCellHTML = this.cellmanager.getCellHTML(longestCell);
let $div = document.createElement('div');
$div.innerHTML = $longestCellHTML;
let cellText = $div.querySelector('.dt-cell__content').textContent;
let {
borderLeftWidth,
borderRightWidth,
paddingLeft,
paddingRight
} = $.getStyle(this.bodyScrollable.querySelector('.dt-cell__content'));
let padding = [borderLeftWidth, borderRightWidth, paddingLeft, paddingRight]
.map(parseFloat)
.reduce((sum, val) => sum + val);
let width = $.measureTextWidth(cellText) + padding;
this.datamanager.updateColumn(colIndex, { width });
this.setColumnHeaderWidth(colIndex);
this.setColumnWidth(colIndex);
});
}
bindMoveColumn() {
let initialized;
if (this.options.disableReorderColumn) return;
const initialize = () => {
if (initialized) {
$.off(document.body, 'mousemove', initialize);
return;
}
const ready = $('.dt-cell', this.header);
if (!ready) return;
const $parent = $('.dt-row', this.header);
const $parent = $('.dt-row', this.header);
this.sortable = Sortable.create($parent, {
onEnd: (e) => {
const {
oldIndex,
newIndex
} = e;
const $draggedCell = e.item;
const {
colIndex
} = $.data($draggedCell);
if (+colIndex === newIndex) return;
this.sortable = Sortable.create($parent, {
onEnd: (e) => {
const {
oldIndex,
newIndex
} = e;
const $draggedCell = e.item;
const {
colIndex
} = $.data($draggedCell);
if (+colIndex === newIndex) return;
this.switchColumn(oldIndex, newIndex);
},
preventOnFilter: false,
filter: '.dt-cell__resize-handle, .dt-dropdown',
chosenClass: 'dt-cell--dragging',
animation: 150
});
};
$.on(document.body, 'mousemove', initialize);
this.switchColumn(oldIndex, newIndex);
},
preventOnFilter: false,
filter: '.dt-cell__resize-handle, .dt-dropdown',
chosenClass: 'dt-cell--dragging',
animation: 150
});
}
sortColumn(colIndex, nextSortOrder) {
@ -266,6 +304,8 @@ export default class ColumnManager {
}
toggleFilter(flag) {
if (!this.options.inlineFilters) return;
let showFilter;
if (flag === undefined) {
showFilter = !this.isFilterShown;
@ -286,31 +326,48 @@ export default class ColumnManager {
focusFilter(colIndex) {
if (!this.isFilterShown) return;
const $filterInput = $(`[data-col-index="${colIndex}"] .dt-filter`, this.$filterRow);
const $filterInput = $(`.dt-cell--col-${colIndex} .dt-filter`, this.$filterRow);
$filterInput.focus();
}
bindFilter() {
if (!this.options.inlineFilters) return;
const handler = e => {
const $filterCell = $.closest('.dt-cell', e.target);
const {
colIndex
} = $.data($filterCell);
const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex)
.then(({
rowsToHide,
rowsToShow
}) => {
this.rowmanager.hideRows(rowsToHide);
this.rowmanager.showRows(rowsToShow);
});
this.applyFilter(this.getAppliedFilters());
};
$.on(this.header, 'keydown', '.dt-filter', debounce(handler, 300));
}
applyFilter(filters) {
this.datamanager.filterRows(filters)
.then(({
rowsToShow
}) => {
this.rowmanager.showRows(rowsToShow);
});
}
getAppliedFilters() {
const filters = {};
$.each('.dt-filter', this.header).map((input) => {
const value = input.value;
if (value) {
filters[input.dataset.colIndex] = value;
}
});
return filters;
}
applyDefaultSortOrder() {
// sort rows if any 1 column has a default sortOrder set
const columnsToSort = this.getColumns().filter(col => col.sortOrder !== 'none');
if (columnsToSort.length === 1) {
const column = columnsToSort[0];
this.sortColumn(column.colIndex, column.sortOrder);
}
}
sortRows(colIndex, sortOrder) {
return this.datamanager.sortRows(colIndex, sortOrder);
}
@ -325,11 +382,9 @@ export default class ColumnManager {
setColumnWidth(colIndex, width) {
colIndex = +colIndex;
this._columnWidthMap = this._columnWidthMap || [];
let columnWidth = width || this.getColumn(colIndex).width;
let index = this._columnWidthMap[colIndex];
const selector = [
`.dt-cell__content--col-${colIndex}`,
`.dt-cell__edit--col-${colIndex}`
@ -339,11 +394,7 @@ export default class ColumnManager {
width: columnWidth + 'px'
};
index = this.style.setStyle(selector, styles, index);
if (index !== undefined) {
this._columnWidthMap[colIndex] = index;
}
this.style.setStyle(selector, styles);
}
setColumnHeaderWidth(colIndex) {
@ -381,17 +432,24 @@ export default class ColumnManager {
}
getDropdownHTML() {
const { dropdownButton, headerDropdown: dropdownItems } = this.options;
const { dropdownButton } = this.options;
return `
<div class="dt-dropdown">
<div class="dt-dropdown__toggle">${dropdownButton}</div>
<div class="dt-dropdown__list">
${dropdownItems.map((d, i) => `
<div class="dt-dropdown__list-item" data-index="${i}">${d.label}</div>
`).join('')}
</div>
</div>
`;
}
getDropdownListHTML() {
const { headerDropdown: dropdownItems } = this.options;
return `
<div class="dt-dropdown__list">
${dropdownItems.map((d, i) => `
<div class="dt-dropdown__list-item" data-index="${i}">${d.label}</div>
`).join('')}
</div>
`;
}
}

11
src/dark.css Normal file
View File

@ -0,0 +1,11 @@
.datatable {
--dt-border-color: #424242;
--dt-light-bg: #2e3538;
--dt-text-color: #dfe2e5;
--dt-text-light: #dfe2e5;
--dt-cell-bg: #1c1f20;
--dt-focus-border-width: 1px;
--dt-selection-highlight-color: var(--dt-light-bg);
--dt-toast-message-border: 1px solid var(--dt-border-color);
--dt-header-cell-bg: #262c2e;
}

View File

@ -11,7 +11,7 @@ export default class DataManager {
this.sortRows = nextTick(this.sortRows, this);
this.switchColumn = nextTick(this.switchColumn, this);
this.removeColumn = nextTick(this.removeColumn, this);
this.filterRows = nextTick(this.filterRows, this);
this.options.filterRows = nextTick(this.options.filterRows, this);
}
init(data, columns) {
@ -29,10 +29,10 @@ export default class DataManager {
this.rows = [];
this.prepareColumns();
this.prepareRows();
this.validateData(this.data);
this.rows = this.prepareRows(this.data);
this.prepareTreeRows();
this.prepareRowView();
this.prepareNumericColumns();
}
@ -134,7 +134,7 @@ export default class DataManager {
this.columns = this.columns.map((column, i) => {
const cellValue = row0[i].content;
if (!column.align && cellValue && isNumeric(cellValue)) {
if (!column.align && isNumeric(cellValue)) {
column.align = 'right';
}
@ -142,10 +142,8 @@ export default class DataManager {
});
}
prepareRows() {
this.validateData(this.data);
this.rows = this.data.map((d, i) => {
prepareRows(data) {
return data.map((d, i) => {
const index = this._getNextRowCount();
let row = [];
@ -194,6 +192,7 @@ export default class DataManager {
row.meta.isLeaf = !nextRow ||
notSet(nextRow.meta.indent) ||
nextRow.meta.indent <= row.meta.indent;
row.meta.isTreeNodeClose = false;
}
});
}
@ -243,8 +242,9 @@ export default class DataManager {
appendRows(rows) {
this.validateData(rows);
this.rows.push(...this.prepareRows(rows));
this.rows = this.rows.concat(this.prepareRows(rows));
this.prepareTreeRows();
this.prepareRowView();
}
sortRows(colIndex, sortOrder = 'none') {
@ -280,8 +280,11 @@ export default class DataManager {
this.rowViewOrder.sort((a, b) => {
const aIndex = a;
const bIndex = b;
const aContent = this.getCell(colIndex, a).content;
const bContent = this.getCell(colIndex, b).content;
let aContent = this.getCell(colIndex, a).content;
let bContent = this.getCell(colIndex, b).content;
aContent = aContent == null ? '' : aContent;
bContent = bContent == null ? '' : bContent;
if (sortOrder === 'none') {
return aIndex - bIndex;
@ -423,32 +426,37 @@ export default class DataManager {
return column;
}
filterRows(keyword, colIndex) {
let rowsToHide = [];
let rowsToShow = [];
const cells = this.rows.map(row => row[colIndex]);
filterRows(filters) {
return this.options.filterRows(this.rows, filters, this)
.then(result => {
if (!result) {
result = this.getAllRowIndices();
}
cells.forEach(cell => {
const hay = String(cell.content || '').toLowerCase();
const needle = (keyword || '').toLowerCase();
if (!result.then) {
result = Promise.resolve(result);
}
if (!needle || hay.includes(needle)) {
rowsToShow.push(cell.rowIndex);
} else {
rowsToHide.push(cell.rowIndex);
}
});
return result.then(rowsToShow => {
this._filteredRows = rowsToShow;
this._filteredRows = rowsToShow;
const rowsToHide = this.getAllRowIndices()
.filter(index => !rowsToShow.includes(index));
return {
rowsToHide,
rowsToShow
};
return {
rowsToHide,
rowsToShow
};
});
});
}
getFilteredRowIndices() {
return this._filteredRows || this.rows.map(row => row.meta.rowIndex);
return this._filteredRows || this.getAllRowIndices();
}
getAllRowIndices() {
return this.rows.map(row => row.meta.rowIndex);
}
getRowCount() {

View File

@ -6,7 +6,18 @@ import RowManager from './rowmanager';
import BodyRenderer from './body-renderer';
import Style from './style';
import Keyboard from './keyboard';
import DEFAULT_OPTIONS from './defaults';
import TranslationManager from './translationmanager';
import getDefaultOptions from './defaults';
let defaultComponents = {
DataManager,
CellManager,
ColumnManager,
RowManager,
BodyRenderer,
Style,
Keyboard
};
class DataTable {
constructor(wrapper, options) {
@ -21,36 +32,48 @@ class DataTable {
throw new Error('Invalid argument given for `wrapper`');
}
this.initializeTranslations(options);
this.setDefaultOptions();
this.buildOptions(options);
this.prepare();
this.style = new Style(this);
this.keyboard = new Keyboard(this.wrapper);
this.datamanager = new DataManager(this.options);
this.rowmanager = new RowManager(this);
this.columnmanager = new ColumnManager(this);
this.cellmanager = new CellManager(this);
this.bodyRenderer = new BodyRenderer(this);
this.initializeComponents();
if (this.options.data) {
this.refresh();
this.columnmanager.applyDefaultSortOrder();
}
}
initializeTranslations(options) {
this.language = options.language || 'en';
this.translationManager = new TranslationManager(this.language);
if (options.translations) {
this.translationManager.addTranslations(options.translations);
}
}
setDefaultOptions() {
this.DEFAULT_OPTIONS = getDefaultOptions(this);
}
buildOptions(options) {
this.options = this.options || {};
this.options = Object.assign(
{}, DEFAULT_OPTIONS,
{}, this.DEFAULT_OPTIONS,
this.options || {}, options
);
this.options.headerDropdown
.push(...(options.headerDropdown || []));
options.headerDropdown = options.headerDropdown || [];
this.options.headerDropdown = [
...this.DEFAULT_OPTIONS.headerDropdown,
...options.headerDropdown
];
// custom user events
this.events = Object.assign(
{}, DEFAULT_OPTIONS.events,
{}, this.DEFAULT_OPTIONS.events,
this.options.events || {},
options.events || {}
);
@ -62,12 +85,34 @@ class DataTable {
this.unfreeze();
}
initializeComponents() {
let components = Object.assign({}, defaultComponents, this.options.overrideComponents);
let {
Style,
Keyboard,
DataManager,
RowManager,
ColumnManager,
CellManager,
BodyRenderer
} = components;
this.style = new Style(this);
this.keyboard = new Keyboard(this.wrapper);
this.datamanager = new DataManager(this.options);
this.rowmanager = new RowManager(this);
this.columnmanager = new ColumnManager(this);
this.cellmanager = new CellManager(this);
this.bodyRenderer = new BodyRenderer(this);
}
prepareDom() {
this.wrapper.innerHTML = `
<div class="datatable">
<table class="dt-header">
</table>
<div class="dt-scrollable">
<div class="datatable" dir="${this.options.direction}">
<div class="datatable-content">
<div class="dt-header"></div>
<div class="dt-scrollable"></div>
<div class="dt-footer"></div>
</div>
<div class="dt-freeze">
<span class="dt-freeze__message">
@ -75,16 +120,19 @@ class DataTable {
</span>
</div>
<div class="dt-toast"></div>
<div class="dt-dropdown-container"></div>
<textarea class="dt-paste-target"></textarea>
</div>
`;
this.datatableWrapper = $('.datatable', this.wrapper);
this.header = $('.dt-header', this.wrapper);
this.footer = $('.dt-footer', this.wrapper);
this.bodyScrollable = $('.dt-scrollable', this.wrapper);
this.freezeContainer = $('.dt-freeze', this.wrapper);
this.toastMessage = $('.dt-toast', this.wrapper);
this.pasteTarget = $('.dt-paste-target', this.wrapper);
this.dropdownContainer = $('.dt-dropdown-container', this.wrapper);
}
refresh(data, columns) {
@ -96,6 +144,7 @@ class DataTable {
destroy() {
this.wrapper.innerHTML = '';
this.style.destroy();
this.fireEvent('onDestroy');
}
appendRows(rows) {
@ -189,7 +238,22 @@ class DataTable {
}
fireEvent(eventName, ...args) {
this.events[eventName].apply(this, args);
// fire internalEventHandlers if any
// and then user events
const handlers = [
...(this._internalEventHandlers[eventName] || []),
this.events[eventName]
].filter(Boolean);
for (let handler of handlers) {
handler.apply(this, args);
}
}
on(event, handler) {
this._internalEventHandlers = this._internalEventHandlers || {};
this._internalEventHandlers[event] = this._internalEventHandlers[event] || [];
this._internalEventHandlers[event].push(handler);
}
log() {
@ -197,6 +261,10 @@ class DataTable {
console.log.apply(console, arguments);
}
}
translate(str, args) {
return this.translationManager.translate(str, args);
}
}
DataTable.instances = 0;

View File

@ -1,56 +1,73 @@
export default {
columns: [],
data: [],
dropdownButton: '▼',
headerDropdown: [
{
label: 'Sort Ascending',
action: function (column) {
this.sortColumn(column.colIndex, 'asc');
import filterRows from './filterRows';
import icons from './icons';
export default function getDefaultOptions(instance) {
return {
columns: [],
data: [],
dropdownButton: icons.chevronDown,
headerDropdown: [
{
label: instance.translate('Sort Ascending'),
action: function (column) {
this.sortColumn(column.colIndex, 'asc');
}
},
{
label: instance.translate('Sort Descending'),
action: function (column) {
this.sortColumn(column.colIndex, 'desc');
}
},
{
label: instance.translate('Reset sorting'),
action: function (column) {
this.sortColumn(column.colIndex, 'none');
}
},
{
label: instance.translate('Remove column'),
action: function (column) {
this.removeColumn(column.colIndex);
}
}
],
events: {
onRemoveColumn(column) {},
onSwitchColumn(column1, column2) {},
onSortColumn(column) {},
onCheckRow(row) {},
onDestroy() {}
},
{
label: 'Sort Descending',
action: function (column) {
this.sortColumn(column.colIndex, 'desc');
}
hooks: {
columnTotal: null
},
{
label: 'Reset sorting',
action: function (column) {
this.sortColumn(column.colIndex, 'none');
}
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
},
{
label: 'Remove column',
action: function (column) {
this.removeColumn(column.colIndex);
}
}
],
events: {
onRemoveColumn(column) {},
onSwitchColumn(column1, column2) {},
onSortColumn(column) {},
onCheckRow(row) {}
},
sortIndicator: {
asc: '↑',
desc: '↓',
none: ''
},
freezeMessage: '',
getEditor: null,
serialNoColumn: true,
checkboxColumn: false,
clusterize: true,
logs: false,
layout: 'fixed', // fixed, fluid, ratio
noDataMessage: 'No Data',
cellHeight: null,
inlineFilters: false,
treeView: false,
checkedRowStatus: true,
dynamicRowHeight: false,
pasteFromClipboard: false
overrideComponents: {
// ColumnManager: CustomColumnManager
},
filterRows: filterRows,
freezeMessage: '',
getEditor: null,
serialNoColumn: true,
checkboxColumn: false,
clusterize: true,
logs: false,
layout: 'fixed', // fixed, fluid, ratio
noDataMessage: instance.translate('No Data'),
cellHeight: 40,
minimumColumnWidth: 30,
inlineFilters: false,
treeView: false,
checkedRowStatus: true,
dynamicRowHeight: false,
pasteFromClipboard: false,
showTotalRow: false,
direction: 'ltr',
disableReorderColumn: false
};
};

View File

@ -138,6 +138,10 @@ $.removeStyle = (elements, styleProps) => {
};
$.getStyle = (element, prop) => {
if (!prop) {
return getComputedStyle(element);
}
let val = getComputedStyle(element)[prop];
if (['width', 'height'].includes(prop)) {
@ -180,7 +184,16 @@ $.scrollTop = function scrollTop(element, pixels) {
});
};
$.scrollbarWidth = function scrollbarWidth() {
$.scrollbarSize = function scrollbarSize() {
if (!$.scrollBarSizeValue) {
$.scrollBarSizeValue = getScrollBarSize();
}
return $.scrollBarSizeValue;
};
function getScrollBarSize() {
// assume scrollbar width and height would be the same
// Create the measurement node
const scrollDiv = document.createElement('div');
$.style(scrollDiv, {
@ -199,4 +212,24 @@ $.scrollbarWidth = function scrollbarWidth() {
document.body.removeChild(scrollDiv);
return scrollbarWidth;
}
$.hasVerticalOverflow = function (element) {
return element.scrollHeight > element.offsetHeight + 10;
};
$.hasHorizontalOverflow = function (element) {
return element.scrollWidth > element.offsetWidth + 10;
};
$.measureTextWidth = function (text) {
const div = document.createElement('div');
div.style.position = 'absolute';
div.style.visibility = 'hidden';
div.style.height = 'auto';
div.style.width = 'auto';
div.style.whiteSpace = 'nowrap';
div.innerText = text;
document.body.appendChild(div);
return div.clientWidth + 1;
};

209
src/filterRows.js Normal file
View File

@ -0,0 +1,209 @@
import { isNumber, stripHTML } from './utils';
import CellManager from './cellmanager';
export default function filterRows(rows, filters, data) {
let filteredRowIndices = [];
if (Object.keys(filters).length === 0) {
return rows.map(row => row.meta.rowIndex);
}
for (let colIndex in filters) {
const keyword = filters[colIndex];
const filteredRows = filteredRowIndices.length ?
filteredRowIndices.map(i => rows[i]) :
rows;
const cells = filteredRows.map(row => row[colIndex]);
let filter = guessFilter(keyword);
let filterMethod = getFilterMethod(rows, data, filter);
if (filterMethod) {
filteredRowIndices = filterMethod(filter.text, cells);
} else {
filteredRowIndices = cells.map(cell => cell.rowIndex);
}
}
return filteredRowIndices;
};
function getFilterMethod(rows, allData, filter) {
const getFormattedValue = cell => {
let formatter = CellManager.getCustomCellFormatter(cell);
let rowData = rows[cell.rowIndex];
if (allData && allData.data && allData.data.length) {
rowData = allData.data[cell.rowIndex];
}
if (formatter && cell.content) {
cell.html = formatter(cell.content, rows[cell.rowIndex], cell.column, rowData, filter);
return stripHTML(cell.html);
}
return cell.content || '';
};
const stringCompareValue = cell =>
String(stripHTML(cell.html || '') || getFormattedValue(cell)).toLowerCase();
const numberCompareValue = cell => parseFloat(cell.content);
const getCompareValues = (cell, keyword) => {
if (cell.column.compareValue) {
const compareValues = cell.column.compareValue(cell, keyword);
if (compareValues && Array.isArray(compareValues)) return compareValues;
}
// check if it can be converted to number
const float = numberCompareValue(cell);
if (!isNaN(float)) {
return [float, keyword];
}
return [stringCompareValue(cell), keyword];
};
let filterMethodMap = {
contains(keyword, cells) {
return cells
.filter(cell => {
const needle = (keyword || '').toLowerCase();
return !needle ||
(cell.content || '').toLowerCase().includes(needle) ||
stringCompareValue(cell).includes(needle);
})
.map(cell => cell.rowIndex);
},
greaterThan(keyword, cells) {
return cells
.filter(cell => {
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
return compareValue > keywordValue;
})
.map(cell => cell.rowIndex);
},
lessThan(keyword, cells) {
return cells
.filter(cell => {
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
return compareValue < keywordValue;
})
.map(cell => cell.rowIndex);
},
equals(keyword, cells) {
return cells
.filter(cell => {
const value = parseFloat(cell.content);
return value === keyword;
})
.map(cell => cell.rowIndex);
},
notEquals(keyword, cells) {
return cells
.filter(cell => {
const value = parseFloat(cell.content);
return value !== keyword;
})
.map(cell => cell.rowIndex);
},
range(rangeValues, cells) {
return cells
.filter(cell => {
const values1 = getCompareValues(cell, rangeValues[0]);
const values2 = getCompareValues(cell, rangeValues[1]);
const value = values1[0];
return value >= values1[1] && value <= values2[1];
})
.map(cell => cell.rowIndex);
},
containsNumber(keyword, cells) {
return cells
.filter(cell => {
let number = parseFloat(keyword, 10);
let string = keyword;
let hayNumber = numberCompareValue(cell);
let hayString = stringCompareValue(cell);
return number === hayNumber || hayString.includes(string);
})
.map(cell => cell.rowIndex);
}
};
return filterMethodMap[filter.type];
}
function guessFilter(keyword = '') {
if (keyword.length === 0) return {};
let compareString = keyword;
if (['>', '<', '='].includes(compareString[0])) {
compareString = keyword.slice(1);
} else if (compareString.startsWith('!=')) {
compareString = keyword.slice(2);
}
if (keyword.startsWith('>')) {
if (compareString) {
return {
type: 'greaterThan',
text: compareString.trim()
};
}
}
if (keyword.startsWith('<')) {
if (compareString) {
return {
type: 'lessThan',
text: compareString.trim()
};
}
}
if (keyword.startsWith('=')) {
if (isNumber(compareString)) {
return {
type: 'equals',
text: Number(keyword.slice(1).trim())
};
}
}
if (isNumber(compareString)) {
return {
type: 'containsNumber',
text: compareString
};
}
if (keyword.startsWith('!=')) {
if (isNumber(compareString)) {
return {
type: 'notEquals',
text: Number(keyword.slice(2).trim())
};
}
}
if (keyword.split(':').length === 2 && keyword.split(':').every(v => isNumber(v.trim()))) {
compareString = keyword.split(':');
return {
type: 'range',
text: compareString.map(v => v.trim())
};
}
return {
type: 'contains',
text: compareString.toLowerCase()
};
}

10
src/icons.js Normal file
View File

@ -0,0 +1,10 @@
/* eslint-disable max-len */
// Icons from https://feathericons.com/
let icons = {
chevronDown: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>',
chevronRight: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>'
};
export default icons;

View File

@ -3,7 +3,9 @@ import {
makeDataAttributeString,
nextTick,
ensureArray,
linkProperties
linkProperties,
uniq,
numberSortAsc
} from './utils';
export default class RowManager {
@ -14,7 +16,8 @@ export default class RowManager {
'fireEvent',
'wrapper',
'bodyScrollable',
'bodyRenderer'
'bodyRenderer',
'style'
]);
this.bindEvents();
@ -39,7 +42,7 @@ export default class RowManager {
// map of checked rows
this.checkMap = [];
$.on(this.wrapper, 'click', '.dt-cell[data-col-index="0"] [type="checkbox"]', (e, $checkbox) => {
$.on(this.wrapper, 'click', '.dt-cell--col-0 [type="checkbox"]', (e, $checkbox) => {
const $cell = $checkbox.closest('.dt-cell');
const {
rowIndex,
@ -64,7 +67,7 @@ export default class RowManager {
const _row = this.datamanager.updateRow(row, rowIndex);
_row.forEach(cell => {
this.cellmanager.refreshCell(cell);
this.cellmanager.refreshCell(cell, true);
});
}
@ -91,8 +94,7 @@ export default class RowManager {
checkRow(rowIndex, toggle) {
const value = toggle ? 1 : 0;
const selector = rowIndex =>
`.dt-cell[data-row-index="${rowIndex}"][data-col-index="0"] [type="checkbox"]`;
const selector = rowIndex => `.dt-cell--0-${rowIndex} [type="checkbox"]`;
// update internal map
this.checkMap[rowIndex] = value;
// set checkbox value explicitly
@ -111,18 +113,25 @@ export default class RowManager {
// update internal map
if (toggle) {
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
if (this.datamanager._filteredRows) {
this.datamanager._filteredRows.forEach(f => {
this.checkRow(f, toggle);
});
} else {
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
}
} else {
this.checkMap = [];
}
// set checkbox value
$.each('.dt-cell[data-col-index="0"] [type="checkbox"]', this.bodyScrollable)
$.each('.dt-cell--col-0 [type="checkbox"]', this.bodyScrollable)
.map(input => {
input.checked = toggle;
});
// highlight all
this.highlightAll(toggle);
this.showCheckStatus();
this.fireEvent('onCheckRow');
}
showCheckStatus() {
@ -130,7 +139,10 @@ export default class RowManager {
const checkedRows = this.getCheckedRows();
const count = checkedRows.length;
if (count > 0) {
this.bodyRenderer.showToastMessage(`${count} row${count > 1 ? 's' : ''} selected`);
let message = this.instance.translate('{count} rows selected', {
count: count
});
this.bodyRenderer.showToastMessage(message);
} else {
this.bodyRenderer.clearToastMessage();
}
@ -173,42 +185,104 @@ export default class RowManager {
}
}
hideRows(rowIndices) {
rowIndices = ensureArray(rowIndices);
rowIndices.map(rowIndex => {
const $tr = this.getRow$(rowIndex);
$tr.classList.add('dt-row--hide');
});
}
showRows(rowIndices) {
rowIndices = ensureArray(rowIndices);
rowIndices.map(rowIndex => {
const $tr = this.getRow$(rowIndex);
$tr.classList.remove('dt-row--hide');
});
const rows = rowIndices.map(rowIndex => this.datamanager.getRow(rowIndex));
this.bodyRenderer.renderRows(rows);
}
showAllRows() {
const rowIndices = this.datamanager.getAllRowIndices();
this.showRows(rowIndices);
}
getChildrenToShowForNode(rowIndex) {
const row = this.datamanager.getRow(rowIndex);
row.meta.isTreeNodeClose = false;
return this.datamanager.getImmediateChildren(rowIndex);
}
openSingleNode(rowIndex) {
const rowsToShow = this.datamanager.getImmediateChildren(rowIndex);
const childrenToShow = this.getChildrenToShowForNode(rowIndex);
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
this.showRows(rowsToShow);
this.cellmanager.toggleTreeButton(rowIndex, true);
}
getChildrenToHideForNode(rowIndex) {
const row = this.datamanager.getRow(rowIndex);
row.meta.isTreeNodeClose = true;
const rowsToHide = this.datamanager.getChildren(rowIndex);
rowsToHide.forEach(rowIndex => {
const row = this.datamanager.getRow(rowIndex);
if (!row.meta.isLeaf) {
row.meta.isTreeNodeClose = true;
}
});
return rowsToHide;
}
closeSingleNode(rowIndex) {
const children = this.datamanager.getImmediateChildren(rowIndex);
children.forEach(childIndex => {
const row = this.datamanager.getRow(childIndex);
if (row.meta.isLeaf) {
// close
this.hideRows(childIndex);
this.cellmanager.toggleTreeButton(childIndex, false);
} else {
this.closeSingleNode(childIndex);
this.hideRows(childIndex);
const rowsToHide = this.getChildrenToHideForNode(rowIndex);
const visibleRows = this.bodyRenderer.visibleRowIndices;
const rowsToShow = visibleRows
.filter(rowIndex => !rowsToHide.includes(rowIndex))
.sort(numberSortAsc);
this.showRows(rowsToShow);
}
expandAllNodes() {
let rows = this.datamanager.getRows();
let rootNodes = rows.filter(row => !row.meta.isLeaf);
const childrenToShow = rootNodes.map(row => this.getChildrenToShowForNode(row.meta.rowIndex)).flat();
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
this.showRows(rowsToShow);
}
collapseAllNodes() {
let rows = this.datamanager.getRows();
let rootNodes = rows.filter(row => row.meta.indent === 0);
const rowsToHide = rootNodes.map(row => this.getChildrenToHideForNode(row.meta.rowIndex)).flat();
const visibleRows = this.bodyRenderer.visibleRowIndices;
const rowsToShow = visibleRows
.filter(rowIndex => !rowsToHide.includes(rowIndex))
.sort(numberSortAsc);
this.showRows(rowsToShow);
}
setTreeDepth(depth) {
let rows = this.datamanager.getRows();
const rowsToOpen = rows.filter(row => row.meta.indent < depth);
const rowsToClose = rows.filter(row => row.meta.indent >= depth);
const rowsToHide = rowsToClose.filter(row => row.meta.indent > depth);
rowsToClose.forEach(row => {
if (!row.meta.isLeaf) {
row.meta.isTreeNodeClose = true;
}
});
this.cellmanager.toggleTreeButton(rowIndex, false);
rowsToOpen.forEach(row => {
if (!row.meta.isLeaf) {
row.meta.isTreeNodeClose = false;
}
});
const rowsToShow = rows
.filter(row => !rowsToHide.includes(row))
.map(row => row.meta.rowIndex)
.sort(numberSortAsc);
this.showRows(rowsToShow);
}
getRow$(rowIndex) {
@ -255,31 +329,41 @@ export default class RowManager {
getRowHTML(row, props) {
const dataAttr = makeDataAttributeString(props);
let rowIdentifier = props.rowIndex;
if (props.isFilter) {
row = row.map(cell => (Object.assign({}, cell, {
content: this.getFilterInput({
colIndex: cell.colIndex
colIndex: cell.colIndex,
name: cell.name
}),
isFilter: 1,
isHeader: undefined,
editable: false
})));
rowIdentifier = 'filter';
}
if (props.isHeader) {
rowIdentifier = 'header';
}
return `
<tr class="dt-row" ${dataAttr}>
<div class="dt-row dt-row-${rowIdentifier}" ${dataAttr}>
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
</tr>
</div>
`;
}
getFilterInput(props) {
let title = `title="Filter based on ${props.name || 'Index'}"`;
const dataAttr = makeDataAttributeString(props);
return `<input class="dt-filter dt-input" type="text" ${dataAttr} />`;
return `<input class="dt-filter dt-input" type="text" ${dataAttr} tabindex="1"
${props.colIndex === 0 ? 'disabled' : title} />`;
}
selector(rowIndex) {
return `.dt-row[data-row-index="${rowIndex}"]`;
return `.dt-row-${rowIndex}`;
}
}

View File

@ -1,259 +1,308 @@
:root {
--border-color: #d1d8dd;
--primary-color: rgb(82, 146, 247);
--light-bg: #f5f7fa;
--light-red: #FD8B8B;
--light-yellow: #fffce7;
--orange: rgb(255, 160, 10);
--text-color: #000000;
--text-light: #dfe2e5;
--spacer-1: 0.25rem;
--spacer-2: 0.5rem;
--spacer-3: 1rem;
--border-radius: 3px;
--cell-bg: #fff;
--dt-border-color: #d1d8dd;
--dt-primary-color: rgb(82, 146, 247);
--dt-light-bg: #f5f7fa;
--dt-light-red: #FD8B8B;
--dt-light-yellow: #fffce7;
--dt-orange: rgb(255, 160, 10);
--dt-text-color: #000000;
--dt-text-light: #dfe2e5;
--dt-spacer-1: 0.25rem;
--dt-spacer-2: 0.5rem;
--dt-spacer-3: 1rem;
--dt-border-radius: 3px;
--dt-cell-bg: #fff;
--dt-focus-border-width: 2px;
--dt-selection-highlight-color: var(--dt-light-yellow);
--dt-toast-message-border: none;
--dt-header-cell-bg: var(--dt-cell-bg);
}
.datatable {
*, *::after, *::before {
box-sizing: border-box;
}
box-sizing: border-box;
}
}
.datatable {
position: relative;
overflow: auto;
position: relative;
overflow: hidden;
.dt-cell--col-0{
border-left: 1px solid var(--dt-border-color);
}
}
.dt-header {
border-collapse: collapse;
border-bottom: 1px solid var(--border-color);
position: absolute;
top: 0;
left: 0;
background-color: var(--cell-bg);
.dt-header{
border-bottom: 2px solid var(--dt-border-color);
}
.dt-body {
border-collapse: collapse;
.datatable-content{
.dt-header{
display: flex;
}
}
.dt-scrollable {
max-height: 40vw;
overflow: auto;
border-bottom: 1px solid var(--border-color);
height: 40vw;
&--highlight-all {
background-color: var(--light-yellow);
}
&--highlight-all {
background-color: var(--dt-selection-highlight-color);
}
&__no-data {
text-align: center;
padding: var(--spacer-3);
border-left: 1px solid var(--border-color);
border-right: 1px solid var(--border-color);
}
&__no-data {
text-align: center;
padding: var(--dt-spacer-3);
border-left: 1px solid var(--dt-border-color);
border-right: 1px solid var(--dt-border-color);
}
}
.dt-row {
&--highlight {
background-color: var(--light-yellow);
}
display: flex;
&--unhighlight {
background-color: var(--cell-bg);
}
&--highlight .dt-cell {
background-color: var(--dt-selection-highlight-color);
}
&--hide {
display: none;
}
&--unhighlight .dt-cell {
background-color: var(--dt-cell-bg);
}
&--hide {
display: none;
}
&:last-child:not(.dt-row-filter) {
border-bottom: 1px solid var(--dt-border-color);
}
}
.dt-cell {
border: 1px solid var(--border-color);
position: relative;
outline: none;
padding: 0;
border: 1px solid var(--dt-border-color);
border-bottom: none;
border-left: none;
position: relative;
outline: none;
padding: 0;
background-color: var(--dt-cell-bg);
color: var(--dt-text-color);
/*
Fix for firefox and Edge
https://stackoverflow.com/a/16337203
firefox paints td background over border
*/
background-clip: padding-box;
user-select: none;
&__content {
padding: var(--spacer-2);
border: 2px solid transparent;
height: 100%;
user-select: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
&__content {
padding: var(--dt-spacer-2);
border: var(--dt-focus-border-width) solid transparent;
height: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
&__edit {
display: none;
padding: var(--spacer-2);
background-color: var(--cell-bg);
border: 2px solid var(--orange);
z-index: 1;
height: 100%;
}
&__edit {
display: none;
padding: var(--dt-spacer-2);
background-color: var(--dt-cell-bg);
border: var(--dt-focus-border-width) solid var(--dt-orange);
z-index: 1;
height: 100%;
}
&__resize-handle {
opacity: 0;
position: absolute;
right: -3px;
top: 0;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 1;
}
&__resize-handle {
opacity: 0;
position: absolute;
right: -3px;
top: 0;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 1;
}
&--editing &__content {
display: none;
}
&--editing &__content {
display: none;
}
&--editing &__edit {
display: block;
}
&--editing &__edit {
display: block;
}
&--focus &__content {
border-color: var(--primary-color);
}
&--focus &__content {
border-color: var(--dt-primary-color);
}
&--highlight {
background-color: var(--light-bg);
}
&--highlight {
background-color: var(--dt-light-bg);
}
&--dragging {
background-color: var(--light-bg);
}
&--dragging {
background-color: var(--dt-light-bg);
}
&--header &__content {
padding-right: var(--spacer-3);
font-weight: bold;
}
&--header {
background-color: var(--dt-header-cell-bg);
}
&--header:hover .dt-dropdown__toggle {
opacity: 1;
}
&--header:last-child {
border-right: 1px solid var(--dt-border-color);
}
&--tree-close {
.dt-tree-node__toggle:before {
content: '►';
}
}
&--header &__content {
padding-right: var(--dt-spacer-3);
font-weight: bold;
}
&--header:hover .dt-dropdown__toggle {
opacity: 1;
}
&--tree-close {
.icon-open {
display: none;
}
.icon-close {
display: flex;
}
}
&:last-child {
border-right: 1px solid var(--dt-border-color);
}
}
.datatable[dir=rtl] .dt-cell__resize-handle {
right: unset;
left: -3px;
}
.icon-open, .icon-close {
width: 16px;
height: 16px;
}
.icon-open {
display: flex;
}
.icon-close {
display: none;
}
.dt-dropdown {
position: absolute;
right: 10px;
display: inline-flex;
vertical-align: top;
text-align: left;
font-weight: normal;
cursor: pointer;
position: absolute;
right: 10px;
display: inline-flex;
vertical-align: top;
text-align: left;
font-weight: normal;
cursor: pointer;
&__toggle {
opacity: 0;
}
&__toggle {
opacity: 0;
background-color: var(--dt-header-cell-bg);
}
&__list {
display: none;
&__list {
position: fixed;
min-width: 8rem;
z-index: 1;
cursor: pointer;
background-color: var(--dt-cell-bg);
border-radius: var(--dt-border-radius);
padding: var(--dt-spacer-2) 0;
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
}
position: absolute;
min-width: 8rem;
top: 100%;
right: 0;
z-index: 1;
background-color: var(--cell-bg);
border-radius: var(--border-radius);
padding: var(--spacer-2) 0;
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
}
&__list-item {
padding: var(--dt-spacer-2) var(--dt-spacer-3);
&__list-item {
padding: var(--spacer-2) var(--spacer-3);
&:hover {
background-color: var(--dt-light-bg);
}
}
&:hover {
background-color: var(--light-bg);
}
}
&--active &__list {
display: block;
}
&--active &__list {
display: block;
}
}
.dt-tree-node {
display: inline-block;
position: relative;
display: flex;
align-items: center;
position: relative;
&__toggle {
display: inline-block;
position: absolute;
font-size: 10px;
padding: 0 4px;
cursor: pointer;
}
&__toggle:before {
content: '▼';
}
&__toggle {
display: inline-block;
cursor: pointer;
margin-right: 0.2rem;
}
}
.dt-toast {
position: absolute;
bottom: var(--spacer-3);
left: 50%;
transform: translateX(-50%);
position: absolute;
bottom: var(--dt-spacer-3);
left: 50%;
transform: translateX(-50%);
&__message {
display: inline-block;
background-color: rgba(0, 0, 0, 0.8);
color: var(--text-light);
border-radius: var(--border-radius);
padding: var(--spacer-2) var(--spacer-3);
}
&__message {
display: inline-block;
background-color: rgba(0, 0, 0, 0.8);
color: var(--dt-text-light);
border-radius: var(--dt-border-radius);
padding: var(--dt-spacer-2) var(--dt-spacer-3);
border: var(--dt-toast-message-border);
}
}
.dt-input {
outline: none;
width: 100%;
border: none;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
margin: 0;
padding: 0;
outline: none;
width: 100%;
border: none;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
background-color: inherit;
color: inherit;
margin: 0;
padding: 0;
}
.dt-freeze {
display: flex;
justify-content: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: var(--light-bg);
opacity: 0.5;
font-size: 2em;
display: flex;
justify-content: center;
align-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: var(--dt-light-bg);
opacity: 0.5;
font-size: 2em;
&__message {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
&__message {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
}
.dt-paste-target {
position: fixed;
left: -999em;
position: fixed;
left: -999em;
}
body.dt-resize {
cursor: col-resize;
cursor: col-resize;
}
.dt-sticky-col {
position: sticky;
z-index: 1;
left: 0;
}

View File

@ -11,8 +11,8 @@ export default class Style {
linkProperties(this, this.instance, [
'options', 'datamanager', 'columnmanager',
'header', 'bodyScrollable', 'datatableWrapper',
'getColumn'
'header', 'footer', 'bodyScrollable', 'datatableWrapper',
'getColumn', 'bodyRenderer'
]);
this.scopeClass = 'dt-instance-' + instance.constructor.instances;
@ -23,6 +23,7 @@ export default class Style {
this.styleEl = styleEl;
this.bindResizeWindow();
this.bindScrollHeader();
}
get stylesheet() {
@ -30,78 +31,165 @@ export default class Style {
}
bindResizeWindow() {
this.onWindowResize = this.onWindowResize.bind(this);
this.onWindowResize = throttle(this.onWindowResize, 300);
if (this.options.layout === 'fluid') {
$.on(window, 'resize', throttle(() => {
this.distributeRemainingWidth();
this.refreshColumnWidth();
this.compensateScrollbarWidth();
this.setBodyStyle();
}, 300));
$.on(window, 'resize', this.onWindowResize);
}
}
bindScrollHeader() {
this._settingHeaderPosition = false;
$.on(this.bodyScrollable, 'scroll', (e) => {
if (this._settingHeaderPosition) return;
this._settingHeaderPosition = true;
requestAnimationFrame(() => {
const scrollLeft = e.target.scrollLeft;
// Move non-sticky header and footer cells normally
const nonStickyHeaderCells = this.header.querySelectorAll('.dt-cell:not(.dt-sticky-col)');
const nonStickyFooterCells = this.footer.querySelectorAll('.dt-cell:not(.dt-sticky-col)');
nonStickyHeaderCells.forEach(cell => {
$.style(cell, { transform: `translateX(${-scrollLeft}px)` });
});
nonStickyFooterCells.forEach(cell => {
$.style(cell, { transform: `translateX(${-scrollLeft}px)` });
});
const stickyHeaderCells = this.header.querySelectorAll(
'.dt-cell.dt-sticky-col:not(.dt-cell-serial-no):not(.dt-cell-checkbox)'
);
stickyHeaderCells.forEach((headerCell) => {
const colIndex = headerCell.getAttribute('data-col-index');
const bodyCell = this.bodyScrollable.querySelector(`.dt-cell[data-col-index="${colIndex}"]`);
const colLeft = parseFloat(headerCell.style.left) || 0; // get left position of the column
// Find corresponding footer cell
const footerCell = this.footer.querySelector(`.dt-cell[data-col-index="${colIndex}"]`);
if (~~(bodyCell.offsetLeft - scrollLeft) > colLeft) {
headerCell.style.transform = `translateX(${-scrollLeft - 1}px)`;
if (footerCell) {
footerCell.style.transform = `translateX(${scrollLeft ? -scrollLeft - 1 : 0}px)`;
}
} else {
headerCell.style.transform = `translateX(${colLeft - headerCell.offsetLeft}px)`;
if (footerCell) footerCell.style.transform = `translateX(${colLeft - footerCell.offsetLeft}px)`;
}
});
this._settingHeaderPosition = false;
});
});
}
onWindowResize() {
this.distributeRemainingWidth();
this.refreshColumnWidth();
this.setBodyStyle();
}
destroy() {
this.styleEl.remove();
$.off(window, 'resize', this.onWindowResize);
}
setStyle(selector, styleMap, index = -1) {
const styles = Object.keys(styleMap)
.map(prop => {
if (!prop.includes('-')) {
prop = camelCaseToDash(prop);
}
return `${prop}:${styleMap[prop]};`;
})
.join('');
let prefixedSelector = selector
.split(',')
.map(r => `.${this.scopeClass} ${r}`)
.join(',');
let ruleString = `${prefixedSelector} { ${styles} }`;
if (!this.stylesheet) return;
let _index = this.stylesheet.cssRules.length;
if (index !== -1) {
this.stylesheet.deleteRule(index);
_index = index;
setStyle(selector, styleObject) {
if (selector.includes(',')) {
selector.split(',')
.map(s => s.trim())
.forEach(selector => {
this.setStyle(selector, styleObject);
});
return;
}
this.stylesheet.insertRule(ruleString, _index);
return _index; // eslint-disable-line
selector = selector.trim();
if (!selector) return;
this._styleRulesMap = this._styleRulesMap || {};
const prefixedSelector = this._getPrefixedSelector(selector);
if (this._styleRulesMap[prefixedSelector]) {
this.removeStyle(selector);
// merge with old styleobject
styleObject = Object.assign({}, this._styleRulesMap[prefixedSelector], styleObject);
}
const styleString = this._getRuleString(styleObject);
const ruleString = `${prefixedSelector} { ${styleString} }`;
this._styleRulesMap[prefixedSelector] = styleObject;
this.stylesheet.insertRule(ruleString);
}
removeStyle(selector) {
if (selector.includes(',')) {
selector.split(',')
.map(s => s.trim())
.forEach(selector => {
this.removeStyle(selector);
});
return;
}
selector = selector.trim();
if (!selector) return;
// find and remove
const prefixedSelector = this._getPrefixedSelector(selector);
const index = Array.from(this.stylesheet.cssRules)
.findIndex(rule => rule.selectorText === prefixedSelector);
if (index === -1) return;
this.stylesheet.deleteRule(index);
}
_getPrefixedSelector(selector) {
return `.${this.scopeClass} ${selector}`;
}
_getRuleString(styleObject) {
return Object.keys(styleObject)
.map(prop => {
let dashed = prop;
if (!prop.includes('-')) {
dashed = camelCaseToDash(prop);
}
return `${dashed}:${styleObject[prop]};`;
})
.join('');
}
setDimensions() {
this.setHeaderStyle();
this.setCellHeight();
this.setupMinWidth();
this.setupNaturalColumnWidth();
this.setupColumnWidth();
this.distributeRemainingWidth();
this.setColumnStyle();
this.compensateScrollbarWidth();
this.setDefaultCellHeight();
this.setBodyStyle();
}
setHeaderStyle() {
if (this.options.layout === 'fluid') {
// setting width as 0 will ensure that the
// header doesn't take the available space
$.style(this.header, {
width: 0
});
}
$.style(this.header, {
margin: 0
setCellHeight() {
this.setStyle('.dt-cell', {
height: this.options.cellHeight + 'px'
});
}
setupMinWidth() {
$.each('.dt-cell[data-is-header]', this.header).map(col => {
$.each('.dt-cell--header', this.header).map(col => {
const { colIndex } = $.data(col);
const column = this.getColumn(colIndex);
@ -116,8 +204,19 @@ export default class Style {
setupNaturalColumnWidth() {
if (!$('.dt-row')) return;
$.each('.dt-row-header .dt-cell', this.header).map($headerCell => {
const { colIndex } = $.data($headerCell);
const column = this.datamanager.getColumn(colIndex);
let width = $.style($('.dt-cell__content', $headerCell), 'width');
if (typeof width === 'number' && width >= this.options.minimumColumnWidth) {
column.naturalWidth = width;
} else {
column.naturalWidth = this.options.minimumColumnWidth;
}
});
// set initial width as naturally calculated by table's first row
$.each('.dt-row[data-row-index="0"] .dt-cell', this.bodyScrollable).map($cell => {
$.each('.dt-row-0 .dt-cell', this.bodyScrollable).map($cell => {
const {
colIndex
} = $.data($cell);
@ -125,12 +224,11 @@ export default class Style {
let naturalWidth = $.style($('.dt-cell__content', $cell), 'width');
if (column.id === '_rowIndex') {
naturalWidth = this.getRowIndexColumnWidth(naturalWidth);
column.width = naturalWidth;
if (typeof naturalWidth === 'number' && naturalWidth >= column.naturalWidth) {
column.naturalWidth = naturalWidth;
} else {
column.naturalWidth = column.naturalWidth;
}
column.naturalWidth = naturalWidth;
});
}
@ -174,29 +272,32 @@ export default class Style {
if (!column.width) {
column.width = column.naturalWidth;
}
if (column.width < column.minWidth) {
column.width = column.minWidth;
if (column.id === '_rowIndex') {
column.width = this.getRowIndexColumnWidth();
}
if (column.width < this.options.minimumColumnWidth) {
column.width = this.options.minimumColumnWidth;
}
});
}
}
compensateScrollbarWidth() {
requestAnimationFrame(() => {
const scrollbarWidth = $.scrollbarWidth();
const lastCol = this.datamanager.getColumn(-1);
const width = lastCol.width - scrollbarWidth;
this.columnmanager.setColumnWidth(lastCol.colIndex, width);
});
}
distributeRemainingWidth() {
if (this.options.layout !== 'fluid') return;
const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
const headerWidth = $.style(this.header, 'width');
let firstRow = $('.dt-row', this.bodyScrollable);
let firstRowWidth = wrapperWidth;
if (!firstRow) {
let headerRow = $('.dt-row', this.instance.header);
let cellWidths = Array.from(headerRow.children)
.map(cell => cell.offsetWidth);
firstRowWidth = cellWidths.reduce((sum, a) => sum + a, 0);
} else {
firstRowWidth = $.style(firstRow, 'width');
}
const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
const deltaWidth = (wrapperWidth - headerWidth) / resizableColumns.length;
const deltaWidth = (wrapperWidth - firstRowWidth) / resizableColumns.length;
resizableColumns.map(col => {
const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
@ -208,40 +309,25 @@ export default class Style {
});
}
setDefaultCellHeight() {
if (this.options.dynamicRowHeight) return;
if (this.__cellHeightSet) return;
const $firstCell = $('.dt-cell[data-is-header]', this.instance.header);
if (!$firstCell) return;
const height = this.options.cellHeight || $.style($firstCell, 'height');
if (height) {
this.setCellHeight(height);
this.__cellHeightSet = true;
}
}
setCellHeight(height) {
this.setStyle('.dt-cell__content, .dt-cell__edit', {
height: height + 'px'
});
}
setColumnStyle() {
// align columns
this.datamanager.getColumns()
.map(column => {
// alignment
if (['left', 'center', 'right'].includes(column.align)) {
this.setStyle(`.dt-cell--col-${column.colIndex}`, {
'text-align': column.align
});
if (!column.align) {
column.align = 'left';
}
if (!['left', 'center', 'right'].includes(column.align)) {
column.align = 'left';
}
this.setStyle(`.dt-cell--col-${column.colIndex}`, {
'text-align': column.align
});
// width
this.columnmanager.setColumnHeaderWidth(column.colIndex);
this.columnmanager.setColumnWidth(column.colIndex);
});
this.setBodyStyle();
}
refreshColumnWidth() {
@ -253,48 +339,67 @@ export default class Style {
}
setBodyStyle() {
requestAnimationFrame(() => {
const width = $.style(this.header, 'width');
const bodyWidth = $.style(this.datatableWrapper, 'width');
const firstRow = $('.dt-row', this.bodyScrollable);
if (!firstRow) return;
const rowWidth = $.style(firstRow, 'width');
$.style(this.bodyScrollable, {
width: width + 'px'
});
let width = bodyWidth > rowWidth ? rowWidth + 10 : bodyWidth;
$.style(this.bodyScrollable, {
width: width + 'px'
});
const $body = $('.dt-body', this.bodyScrollable);
// remove the body height, so that it resets to it's original
$.removeStyle(this.bodyScrollable, 'height');
if ($body) {
$.style($body, {
height: '0px'
});
// when there are less rows than the container
// adapt the container height
let bodyHeight = $.getStyle(this.bodyScrollable, 'height');
const scrollHeight = (this.bodyRenderer.hyperlist || {})._scrollHeight || Infinity;
const hasHorizontalOverflow = $.hasHorizontalOverflow(this.bodyScrollable);
let height;
if (scrollHeight < bodyHeight) {
height = scrollHeight;
// account for scrollbar size when
// there is horizontal overflow
if (hasHorizontalOverflow) {
height += $.scrollbarSize();
}
$.style(this.bodyScrollable, {
marginTop: $.style(this.header, 'height') + 'px'
height: height + 'px'
});
}
$.style($('table', this.bodyScrollable), {
margin: 0,
width: '100%'
const verticalOverflow = this.bodyScrollable.scrollHeight - this.bodyScrollable.offsetHeight;
if (verticalOverflow < $.scrollbarSize()) {
// if verticalOverflow is less than scrollbar size
// then most likely scrollbar is causing the scroll
// which is not needed
$.style(this.bodyScrollable, {
overflowY: 'hidden'
});
});
}
if (this.options.layout === 'fluid') {
$.style(this.bodyScrollable, {
overflowX: 'hidden'
});
}
}
getColumnHeaderElement(colIndex) {
colIndex = +colIndex;
if (colIndex < 0) return null;
return $(`.dt-cell[data-col-index="${colIndex}"]`, this.header);
return $(`.dt-cell--col-${colIndex}`, this.header);
}
getRowIndexColumnWidth(baseWidth) {
this._rowIndexColumnWidthMap = this._rowIndexColumnWidthMap || {};
getRowIndexColumnWidth() {
const rowCount = this.datamanager.getRowCount();
const digits = (rowCount + '').length;
if (!this._rowIndexColumnWidthMap[digits]) {
// add 8px for each unit
this._rowIndexColumnWidthMap[digits] = baseWidth + ((digits - 1) * 8);
}
return this._rowIndexColumnWidthMap[digits];
const padding = 22;
return $.measureTextWidth(rowCount + '') + padding;
}
}

30
src/translationmanager.js Normal file
View File

@ -0,0 +1,30 @@
import { format } from './utils';
import getTranslations from './translations';
export default class TranslationManager {
constructor(language) {
this.language = language;
this.translations = getTranslations();
}
addTranslations(translations) {
this.translations = Object.assign(this.translations, translations);
}
translate(sourceText, args) {
let translation = (this.translations[this.language] &&
this.translations[this.language][sourceText]) || sourceText;
if (typeof translation === 'object') {
translation = args && args.count ?
this.getPluralizedTranslation(translation, args.count) :
sourceText;
}
return format(translation, args || {});
}
getPluralizedTranslation(translations, count) {
return translations[count] || translations['default'];
}
};

15
src/translations/de.json Normal file
View File

@ -0,0 +1,15 @@
{
"Sort Ascending": "Aufsteigend sortieren",
"Sort Descending": "Absteigend sortieren",
"Reset sorting": "Sortierung zurücksetzen",
"Remove column": "Spalte entfernen",
"No Data": "Keine Daten",
"{count} cells copied": {
"1": "{count} Zelle kopiert",
"default": "{count} Zellen kopiert"
},
"{count} rows selected": {
"1": "{count} Zeile ausgewählt",
"default": "{count} Zeilen ausgewählt"
}
}

15
src/translations/en.json Normal file
View File

@ -0,0 +1,15 @@
{
"Sort Ascending": "Sort Ascending",
"Sort Descending": "Sort Descending",
"Reset sorting": "Reset sorting",
"Remove column": "Remove column",
"No Data": "No Data",
"{count} cells copied": {
"1": "{count} cell copied",
"default": "{count} cells copied"
},
"{count} rows selected": {
"1": "{count} row selected",
"default": "{count} rows selected"
}
}

15
src/translations/fr.json Normal file
View File

@ -0,0 +1,15 @@
{
"Sort Ascending": "Trier par ordre croissant",
"Sort Descending": "Trier par ordre décroissant",
"Reset sorting": "Réinitialiser le tri",
"Remove column": "Supprimer colonne",
"No Data": "Pas de données",
"{count} cells copied": {
"1": "{count} cellule copiée",
"default": "{count} cellules copiées"
},
"{count} rows selected": {
"1": "{count} ligne sélectionnée",
"default": "{count} lignes sélectionnées"
}
}

13
src/translations/index.js Normal file
View File

@ -0,0 +1,13 @@
import en from './en.json';
import de from './de.json';
import fr from './fr.json';
import it from './it.json';
export default function getTranslations() {
return {
en,
de,
fr,
it,
};
};

15
src/translations/it.json Normal file
View File

@ -0,0 +1,15 @@
{
"Sort Ascending": "Ordinamento ascendente",
"Sort Descending": "Ordinamento decrescente",
"Reset sorting": "Azzeramento ordinamento",
"Remove column": "Rimuovi colonna",
"No Data": "Nessun dato",
"{count} cells copied": {
"1": "Copiato {count} cella",
"default": "{count} celle copiate"
},
"{count} rows selected": {
"1": "{count} linea selezionata",
"default": "{count} linee selezionate"
}
}

View File

@ -1,5 +1,6 @@
import _throttle from 'lodash/throttle';
import _debounce from 'lodash/debounce';
import _uniq from 'lodash/uniq';
export function camelCaseToDash(str) {
return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
@ -125,3 +126,42 @@ export function ensureArray(val) {
}
return val;
}
export function uniq(arr) {
return _uniq(arr);
}
export function numberSortAsc(a, b) {
return a - b;
};
export function stripHTML(html) {
return html.replace(/<[^>]*>/g, '');
};
export function format(str, args) {
if (!str) return str;
Object.keys(args).forEach(arg => {
let regex = new RegExp(`{(${arg})}`, 'g');
str = str.replace(regex, args[arg]);
});
return str;
};
export function escapeHTML(txt) {
if (!txt) return '';
let escapeHtmlMapping = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;',
};
return String(txt).replace(/[&<>"'`=/]/g, (char) => escapeHtmlMapping[char] || char);
};

7418
yarn.lock

File diff suppressed because it is too large Load Diff