@wordpress/dataviews 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE.md +788 -0
  3. package/README.md +224 -0
  4. package/build/add-filter.js +90 -0
  5. package/build/add-filter.js.map +1 -0
  6. package/build/constants.js +55 -0
  7. package/build/constants.js.map +1 -0
  8. package/build/dataviews.js +93 -0
  9. package/build/dataviews.js.map +1 -0
  10. package/build/filter-summary.js +137 -0
  11. package/build/filter-summary.js.map +1 -0
  12. package/build/filters.js +75 -0
  13. package/build/filters.js.map +1 -0
  14. package/build/index.js +21 -0
  15. package/build/index.js.map +1 -0
  16. package/build/item-actions.js +185 -0
  17. package/build/item-actions.js.map +1 -0
  18. package/build/lock-unlock.js +18 -0
  19. package/build/lock-unlock.js.map +1 -0
  20. package/build/pagination.js +123 -0
  21. package/build/pagination.js.map +1 -0
  22. package/build/reset-filters.js +33 -0
  23. package/build/reset-filters.js.map +1 -0
  24. package/build/search.js +46 -0
  25. package/build/search.js.map +1 -0
  26. package/build/view-actions.js +223 -0
  27. package/build/view-actions.js.map +1 -0
  28. package/build/view-grid.js +80 -0
  29. package/build/view-grid.js.map +1 -0
  30. package/build/view-list.js +83 -0
  31. package/build/view-list.js.map +1 -0
  32. package/build/view-table.js +286 -0
  33. package/build/view-table.js.map +1 -0
  34. package/build-module/add-filter.js +83 -0
  35. package/build-module/add-filter.js.map +1 -0
  36. package/build-module/constants.js +41 -0
  37. package/build-module/constants.js.map +1 -0
  38. package/build-module/dataviews.js +85 -0
  39. package/build-module/dataviews.js.map +1 -0
  40. package/build-module/filter-summary.js +130 -0
  41. package/build-module/filter-summary.js.map +1 -0
  42. package/build-module/filters.js +67 -0
  43. package/build-module/filters.js.map +1 -0
  44. package/build-module/index.js +3 -0
  45. package/build-module/index.js.map +1 -0
  46. package/build-module/item-actions.js +178 -0
  47. package/build-module/item-actions.js.map +1 -0
  48. package/build-module/lock-unlock.js +9 -0
  49. package/build-module/lock-unlock.js.map +1 -0
  50. package/build-module/pagination.js +115 -0
  51. package/build-module/pagination.js.map +1 -0
  52. package/build-module/reset-filters.js +26 -0
  53. package/build-module/reset-filters.js.map +1 -0
  54. package/build-module/search.js +39 -0
  55. package/build-module/search.js.map +1 -0
  56. package/build-module/view-actions.js +216 -0
  57. package/build-module/view-actions.js.map +1 -0
  58. package/build-module/view-grid.js +72 -0
  59. package/build-module/view-grid.js.map +1 -0
  60. package/build-module/view-list.js +75 -0
  61. package/build-module/view-list.js.map +1 -0
  62. package/build-module/view-table.js +277 -0
  63. package/build-module/view-table.js.map +1 -0
  64. package/build-style/style-rtl.css +325 -0
  65. package/build-style/style.css +325 -0
  66. package/package.json +49 -0
  67. package/src/add-filter.js +106 -0
  68. package/src/constants.js +50 -0
  69. package/src/dataviews.js +99 -0
  70. package/src/filter-summary.js +221 -0
  71. package/src/filters.js +84 -0
  72. package/src/index.js +2 -0
  73. package/src/item-actions.js +211 -0
  74. package/src/lock-unlock.js +10 -0
  75. package/src/pagination.js +144 -0
  76. package/src/reset-filters.js +26 -0
  77. package/src/search.js +38 -0
  78. package/src/stories/fixtures.js +126 -0
  79. package/src/stories/index.story.js +137 -0
  80. package/src/style.scss +245 -0
  81. package/src/view-actions.js +298 -0
  82. package/src/view-grid.js +100 -0
  83. package/src/view-list.js +99 -0
  84. package/src/view-table.js +425 -0
package/src/search.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { useEffect, useRef } from '@wordpress/element';
6
+ import { SearchControl } from '@wordpress/components';
7
+ import { useDebouncedInput } from '@wordpress/compose';
8
+
9
+ export default function Search( { label, view, onChangeView } ) {
10
+ const [ search, setSearch, debouncedSearch ] = useDebouncedInput(
11
+ view.search
12
+ );
13
+ useEffect( () => {
14
+ setSearch( view.search );
15
+ }, [ view ] );
16
+ const onChangeViewRef = useRef( onChangeView );
17
+ useEffect( () => {
18
+ onChangeViewRef.current = onChangeView;
19
+ }, [ onChangeView ] );
20
+ useEffect( () => {
21
+ onChangeViewRef.current( ( currentView ) => ( {
22
+ ...currentView,
23
+ page: 1,
24
+ search: debouncedSearch,
25
+ } ) );
26
+ }, [ debouncedSearch ] );
27
+ const searchLabel = label || __( 'Filter list' );
28
+ return (
29
+ <SearchControl
30
+ __nextHasNoMarginBottom
31
+ onChange={ setSearch }
32
+ value={ search }
33
+ label={ searchLabel }
34
+ placeholder={ searchLabel }
35
+ size="compact"
36
+ />
37
+ );
38
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { trash } from '@wordpress/icons';
5
+ import {
6
+ Button,
7
+ __experimentalText as Text,
8
+ __experimentalHStack as HStack,
9
+ __experimentalVStack as VStack,
10
+ } from '@wordpress/components';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { LAYOUT_TABLE } from '../constants';
16
+
17
+ export const data = [
18
+ {
19
+ id: 1,
20
+ title: 'Apollo',
21
+ description: 'Apollo description',
22
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
23
+ },
24
+ {
25
+ id: 2,
26
+ title: 'Space',
27
+ description: 'Space description',
28
+ image: 'https://live.staticflickr.com/5678/21911065441_92e2d44708_b.jpg',
29
+ },
30
+ {
31
+ id: 3,
32
+ title: 'NASA',
33
+ description: 'NASA photo',
34
+ image: 'https://live.staticflickr.com/742/21712365770_8f70a2c91e_b.jpg',
35
+ },
36
+ {
37
+ id: 4,
38
+ title: 'Neptune',
39
+ description: 'Neptune description',
40
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
41
+ },
42
+ {
43
+ id: 5,
44
+ title: 'Mercury',
45
+ description: 'Mercury description',
46
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
47
+ },
48
+ {
49
+ id: 6,
50
+ title: 'Venus',
51
+ description: 'Venus description',
52
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
53
+ },
54
+ {
55
+ id: 7,
56
+ title: 'Earth',
57
+ description: 'Earth description',
58
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
59
+ },
60
+ {
61
+ id: 8,
62
+ title: 'Mars',
63
+ description: 'Mars description',
64
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
65
+ },
66
+ {
67
+ id: 9,
68
+ title: 'Jupiter',
69
+ description: 'Jupiter description',
70
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
71
+ },
72
+ {
73
+ id: 10,
74
+ title: 'Saturn',
75
+ description: 'Saturn description',
76
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
77
+ },
78
+ {
79
+ id: 11,
80
+ title: 'Uranus',
81
+ description: 'Uranus description',
82
+ image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
83
+ },
84
+ ];
85
+
86
+ export const DEFAULT_VIEW = {
87
+ type: LAYOUT_TABLE,
88
+ search: '',
89
+ page: 1,
90
+ perPage: 10,
91
+ hiddenFields: [ 'image' ],
92
+ layout: {},
93
+ filters: [],
94
+ };
95
+
96
+ export const actions = [
97
+ {
98
+ id: 'delete',
99
+ label: 'Delete item',
100
+ isPrimary: true,
101
+ icon: trash,
102
+ hideModalHeader: true,
103
+ RenderModal: ( { item, closeModal } ) => {
104
+ return (
105
+ <VStack spacing="5">
106
+ <Text>
107
+ { `Are you sure you want to delete "${ item.title }"?` }
108
+ </Text>
109
+ <HStack justify="right">
110
+ <Button variant="tertiary" onClick={ closeModal }>
111
+ Cancel
112
+ </Button>
113
+ <Button variant="primary" onClick={ closeModal }>
114
+ Delete
115
+ </Button>
116
+ </HStack>
117
+ </VStack>
118
+ );
119
+ },
120
+ },
121
+ {
122
+ id: 'secondary',
123
+ label: 'Secondary action',
124
+ callback() {},
125
+ },
126
+ ];
@@ -0,0 +1,137 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useState, useMemo, useCallback } from '@wordpress/element';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { DataViews, LAYOUT_GRID, LAYOUT_TABLE } from '../index';
10
+
11
+ import { DEFAULT_VIEW, actions, data } from './fixtures';
12
+
13
+ const meta = {
14
+ title: 'DataViews (Experimental)/DataViews',
15
+ component: DataViews,
16
+ };
17
+ export default meta;
18
+
19
+ const defaultConfigPerViewType = {
20
+ [ LAYOUT_TABLE ]: {},
21
+ [ LAYOUT_GRID ]: {
22
+ mediaField: 'image',
23
+ primaryField: 'title',
24
+ },
25
+ };
26
+
27
+ function normalizeSearchInput( input = '' ) {
28
+ return input.trim().toLowerCase();
29
+ }
30
+
31
+ const fields = [
32
+ {
33
+ header: 'Image',
34
+ id: 'image',
35
+ render: ( { item } ) => {
36
+ return (
37
+ <img src={ item.image } alt="" style={ { width: '100%' } } />
38
+ );
39
+ },
40
+ width: 50,
41
+ enableSorting: false,
42
+ },
43
+ {
44
+ header: 'Title',
45
+ id: 'title',
46
+ getValue: ( { item } ) => item.title,
47
+ maxWidth: 400,
48
+ enableHiding: false,
49
+ },
50
+ {
51
+ header: 'Description',
52
+ id: 'description',
53
+ getValue: ( { item } ) => item.description,
54
+ maxWidth: 200,
55
+ enableSorting: false,
56
+ },
57
+ ];
58
+
59
+ export const Default = ( props ) => {
60
+ const [ view, setView ] = useState( DEFAULT_VIEW );
61
+ const { shownData, paginationInfo } = useMemo( () => {
62
+ let filteredData = [ ...data ];
63
+ // Handle global search.
64
+ if ( view.search ) {
65
+ const normalizedSearch = normalizeSearchInput( view.search );
66
+ filteredData = filteredData.filter( ( item ) => {
67
+ return [
68
+ normalizeSearchInput( item.title ),
69
+ normalizeSearchInput( item.description ),
70
+ ].some( ( field ) => field.includes( normalizedSearch ) );
71
+ } );
72
+ }
73
+ // Handle sorting.
74
+ if ( view.sort ) {
75
+ const stringSortingFields = [ 'title' ];
76
+ const fieldId = view.sort.field;
77
+ if ( stringSortingFields.includes( fieldId ) ) {
78
+ const fieldToSort = fields.find( ( field ) => {
79
+ return field.id === fieldId;
80
+ } );
81
+ filteredData.sort( ( a, b ) => {
82
+ const valueA = fieldToSort.getValue( { item: a } ) ?? '';
83
+ const valueB = fieldToSort.getValue( { item: b } ) ?? '';
84
+ return view.sort.direction === 'asc'
85
+ ? valueA.localeCompare( valueB )
86
+ : valueB.localeCompare( valueA );
87
+ } );
88
+ }
89
+ }
90
+ // Handle pagination.
91
+ const start = ( view.page - 1 ) * view.perPage;
92
+ const totalItems = filteredData?.length || 0;
93
+ filteredData = filteredData?.slice( start, start + view.perPage );
94
+ return {
95
+ shownData: filteredData,
96
+ paginationInfo: {
97
+ totalItems,
98
+ totalPages: Math.ceil( totalItems / view.perPage ),
99
+ },
100
+ };
101
+ }, [ view ] );
102
+ const onChangeView = useCallback(
103
+ ( viewUpdater ) => {
104
+ let updatedView =
105
+ typeof viewUpdater === 'function'
106
+ ? viewUpdater( view )
107
+ : viewUpdater;
108
+ if ( updatedView.type !== view.type ) {
109
+ updatedView = {
110
+ ...updatedView,
111
+ layout: {
112
+ ...defaultConfigPerViewType[ updatedView.type ],
113
+ },
114
+ };
115
+ }
116
+
117
+ setView( updatedView );
118
+ },
119
+ [ view, setView ]
120
+ );
121
+ return (
122
+ <DataViews
123
+ { ...props }
124
+ paginationInfo={ paginationInfo }
125
+ data={ shownData }
126
+ view={ view }
127
+ fields={ fields }
128
+ onChangeView={ onChangeView }
129
+ />
130
+ );
131
+ };
132
+ Default.args = {
133
+ actions,
134
+ getItemId: ( item ) => item.id,
135
+ isLoading: false,
136
+ supportedLayouts: [ LAYOUT_TABLE, LAYOUT_GRID ],
137
+ };
package/src/style.scss ADDED
@@ -0,0 +1,245 @@
1
+ .dataviews-wrapper {
2
+ width: 100%;
3
+ height: 100%;
4
+ overflow: auto;
5
+ box-sizing: border-box;
6
+ scroll-padding-bottom: $grid-unit-80;
7
+
8
+ > div {
9
+ min-height: 100%;
10
+ }
11
+ }
12
+
13
+ .dataviews__filters-view-actions {
14
+ padding: $grid-unit-15 $grid-unit-40;
15
+ }
16
+
17
+ .dataviews-pagination {
18
+ margin-top: auto;
19
+ position: sticky;
20
+ bottom: 0;
21
+ background-color: $white;
22
+ padding: $grid-unit-15 $grid-unit-40;
23
+ border-top: $border-width solid $gray-100;
24
+ color: $gray-700;
25
+ }
26
+
27
+ .dataviews-filters-options {
28
+ margin: $grid-unit-40 0 $grid-unit-20;
29
+ }
30
+
31
+ .dataviews-table-view {
32
+ width: 100%;
33
+ text-indent: 0;
34
+ border-color: inherit;
35
+ border-collapse: collapse;
36
+ position: relative;
37
+ color: $gray-700;
38
+
39
+ a {
40
+ text-decoration: none;
41
+ color: $gray-900;
42
+ font-weight: 500;
43
+ }
44
+ th {
45
+ text-align: left;
46
+ color: var(--wp-components-color-foreground, $gray-900);
47
+ font-weight: normal;
48
+ font-size: $default-font-size;
49
+ }
50
+ td,
51
+ th {
52
+ padding: $grid-unit-15;
53
+ min-width: 160px;
54
+ &[data-field-id="actions"] {
55
+ text-align: right;
56
+ }
57
+ }
58
+ tr {
59
+ border-bottom: 1px solid $gray-100;
60
+
61
+ td:first-child,
62
+ th:first-child {
63
+ padding-left: $grid-unit-40;
64
+ }
65
+
66
+ td:last-child,
67
+ th:last-child {
68
+ padding-right: $grid-unit-40;
69
+ }
70
+
71
+ &:last-child {
72
+ border-bottom: 0;
73
+ }
74
+ }
75
+ thead {
76
+ tr {
77
+ border: 0;
78
+ }
79
+ th {
80
+ position: sticky;
81
+ top: -1px;
82
+ background-color: lighten($gray-100, 4%);
83
+ box-shadow: inset 0 -#{$border-width} 0 $gray-100;
84
+ border-top: 1px solid $gray-100;
85
+ padding-top: $grid-unit-05;
86
+ padding-bottom: $grid-unit-05;
87
+ }
88
+ }
89
+ }
90
+
91
+ .dataviews-grid-view {
92
+ margin-bottom: $grid-unit-30;
93
+ grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
94
+ padding: 0 $grid-unit-40;
95
+
96
+ @include break-xlarge() {
97
+ grid-template-columns: repeat(3, minmax(0, 1fr)) !important; // Todo: eliminate !important dependency
98
+ }
99
+
100
+ @include break-huge() {
101
+ grid-template-columns: repeat(4, minmax(0, 1fr)) !important; // Todo: eliminate !important dependency
102
+ }
103
+
104
+ .dataviews-view-grid__card {
105
+ h3 { // Todo: A better way to target this
106
+ white-space: nowrap;
107
+ overflow: hidden;
108
+ text-overflow: ellipsis;
109
+ }
110
+ }
111
+
112
+ .dataviews-view-grid__media {
113
+ width: 100%;
114
+ min-height: 200px;
115
+ aspect-ratio: 1/1;
116
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
117
+ border-radius: $radius-block-ui * 2;
118
+ overflow: hidden;
119
+
120
+ > * {
121
+ object-fit: cover;
122
+ width: 100%;
123
+ height: 100%;
124
+ }
125
+ }
126
+
127
+ .dataviews-view-grid__primary-field {
128
+ min-height: $grid-unit-30;
129
+
130
+ a {
131
+ color: $gray-900;
132
+ text-decoration: none;
133
+ font-weight: 500;
134
+ }
135
+ }
136
+
137
+ .dataviews-view-grid__fields {
138
+ position: relative;
139
+ font-size: 12px;
140
+ line-height: 16px;
141
+
142
+ .dataviews-view-grid__field {
143
+ .dataviews-view-grid__field-header {
144
+ color: $gray-700;
145
+ }
146
+ .dataviews-view-grid__field-value {
147
+ color: $gray-900;
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ .dataviews-list-view {
154
+ margin: 0;
155
+
156
+ li {
157
+ border-bottom: $border-width solid $gray-100;
158
+ margin: 0;
159
+ &:first-child {
160
+ border-top: $border-width solid $gray-100;
161
+ }
162
+ &:last-child {
163
+ border-bottom: 0;
164
+ }
165
+ }
166
+
167
+ .dataviews-list-view__item {
168
+ padding: $grid-unit-15 $grid-unit-40;
169
+ cursor: default;
170
+ &:focus,
171
+ &:hover {
172
+ background-color: lighten($gray-100, 3%);
173
+ }
174
+ &:focus {
175
+ box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
176
+ }
177
+ h3 {
178
+ overflow: hidden;
179
+ text-overflow: ellipsis;
180
+ white-space: nowrap;
181
+ }
182
+ }
183
+
184
+ .dataviews-list-view__item-selected,
185
+ .dataviews-list-view__item-selected:hover {
186
+ background-color: $gray-100;
187
+
188
+ &:focus {
189
+ box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
190
+ }
191
+ }
192
+
193
+ .dataviews-list-view__media-wrapper {
194
+ min-width: $grid-unit-40;
195
+ height: $grid-unit-40;
196
+ border-radius: $grid-unit-05;
197
+ overflow: hidden;
198
+ position: relative;
199
+
200
+ &::after {
201
+ content: "";
202
+ position: absolute;
203
+ top: 0;
204
+ left: 0;
205
+ width: 100%;
206
+ height: 100%;
207
+ box-shadow: inset 0 0 0 $border-width rgba(0, 0, 0, 0.1);
208
+ border-radius: $grid-unit-05;
209
+ }
210
+ }
211
+
212
+ .edit-site-page-pages__featured-image,
213
+ .dataviews-list-view__media-placeholder {
214
+ min-width: $grid-unit-40;
215
+ height: $grid-unit-40;
216
+ }
217
+
218
+ .dataviews-list-view__media-placeholder {
219
+ background-color: $gray-200;
220
+ }
221
+
222
+ .dataviews-list-view__fields {
223
+ color: $gray-700;
224
+ overflow: hidden;
225
+ text-overflow: ellipsis;
226
+ white-space: nowrap;
227
+
228
+ .dataviews-list-view__field {
229
+ margin-right: $grid-unit-15;
230
+
231
+ &:last-child {
232
+ margin-right: 0;
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ .dataviews-action-modal {
239
+ z-index: z-index(".dataviews-action-modal");
240
+ }
241
+
242
+ .dataviews-no-results,
243
+ .dataviews-loading {
244
+ padding: 0 $grid-unit-40;
245
+ }