dinocollab-core 1.0.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 (214) hide show
  1. package/README.md +54 -0
  2. package/dist/_virtual/_rollupPluginBabelHelpers.js +431 -0
  3. package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
  4. package/dist/assets/vector-404265a04f4f9c8be1f.webp +0 -0
  5. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@1_d0d2002d9033600b6738d939bd598bc6/node_modules/tslib/tslib.es6.js +46 -0
  6. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@1_d0d2002d9033600b6738d939bd598bc6/node_modules/tslib/tslib.es6.js.map +1 -0
  7. package/dist/src/api-context/alert-global.js +151 -0
  8. package/dist/src/api-context/alert-global.js.map +1 -0
  9. package/dist/src/api-context/drawer-global.js +105 -0
  10. package/dist/src/api-context/drawer-global.js.map +1 -0
  11. package/dist/src/api-context/global-modal.js +87 -0
  12. package/dist/src/api-context/global-modal.js.map +1 -0
  13. package/dist/src/api-context/popover-global.js +102 -0
  14. package/dist/src/api-context/popover-global.js.map +1 -0
  15. package/dist/src/api-context/popover.js +86 -0
  16. package/dist/src/api-context/popover.js.map +1 -0
  17. package/dist/src/api-context/ui.units.js +21 -0
  18. package/dist/src/api-context/ui.units.js.map +1 -0
  19. package/dist/src/components/copy-to-clipboard.js +105 -0
  20. package/dist/src/components/copy-to-clipboard.js.map +1 -0
  21. package/dist/src/components/custom.breadcrumbs.js +61 -0
  22. package/dist/src/components/custom.breadcrumbs.js.map +1 -0
  23. package/dist/src/components/help-tooltip.js +91 -0
  24. package/dist/src/components/help-tooltip.js.map +1 -0
  25. package/dist/src/components/image-with-fallback.js +48 -0
  26. package/dist/src/components/image-with-fallback.js.map +1 -0
  27. package/dist/src/components/text-editor.js +117 -0
  28. package/dist/src/components/text-editor.js.map +1 -0
  29. package/dist/src/form/create.autocomplete.chips.js +218 -0
  30. package/dist/src/form/create.autocomplete.chips.js.map +1 -0
  31. package/dist/src/form/create.date-expired.js +201 -0
  32. package/dist/src/form/create.date-expired.js.map +1 -0
  33. package/dist/src/form/create.date-picker.js +125 -0
  34. package/dist/src/form/create.date-picker.js.map +1 -0
  35. package/dist/src/form/create.form-base.js +135 -0
  36. package/dist/src/form/create.form-base.js.map +1 -0
  37. package/dist/src/form/create.form-comfirm.js +119 -0
  38. package/dist/src/form/create.form-comfirm.js.map +1 -0
  39. package/dist/src/form/create.form-grid-layout.js +177 -0
  40. package/dist/src/form/create.form-grid-layout.js.map +1 -0
  41. package/dist/src/form/create.form-grid-layout.units.js +39 -0
  42. package/dist/src/form/create.form-grid-layout.units.js.map +1 -0
  43. package/dist/src/form/create.input-base.js +260 -0
  44. package/dist/src/form/create.input-base.js.map +1 -0
  45. package/dist/src/form/create.input.file.js +74 -0
  46. package/dist/src/form/create.input.file.js.map +1 -0
  47. package/dist/src/form/create.select-simple.js +104 -0
  48. package/dist/src/form/create.select-simple.js.map +1 -0
  49. package/dist/src/form/create.select-with-api.js +271 -0
  50. package/dist/src/form/create.select-with-api.js.map +1 -0
  51. package/dist/src/form/create.text-editor.js +156 -0
  52. package/dist/src/form/create.text-editor.js.map +1 -0
  53. package/dist/src/form/dino-form.js +42 -0
  54. package/dist/src/form/dino-form.js.map +1 -0
  55. package/dist/src/form/helper.js +157 -0
  56. package/dist/src/form/helper.js.map +1 -0
  57. package/dist/src/form/modal-wrapper.js +75 -0
  58. package/dist/src/form/modal-wrapper.js.map +1 -0
  59. package/dist/src/form/validator.js +186 -0
  60. package/dist/src/form/validator.js.map +1 -0
  61. package/dist/src/hooks/index.js +48 -0
  62. package/dist/src/hooks/index.js.map +1 -0
  63. package/dist/src/index.js +26 -0
  64. package/dist/src/index.js.map +1 -0
  65. package/dist/src/redux/create.hoc-lazy.js +67 -0
  66. package/dist/src/redux/create.hoc-lazy.js.map +1 -0
  67. package/dist/src/redux/dino.js +11 -0
  68. package/dist/src/redux/dino.js.map +1 -0
  69. package/dist/src/redux/types.js +9 -0
  70. package/dist/src/redux/types.js.map +1 -0
  71. package/dist/src/redux/ui.error-page.js +80 -0
  72. package/dist/src/redux/ui.error-page.js.map +1 -0
  73. package/dist/src/redux/vector-404.webp.js +4 -0
  74. package/dist/src/redux/vector-404.webp.js.map +1 -0
  75. package/dist/src/table/context.js +12 -0
  76. package/dist/src/table/context.js.map +1 -0
  77. package/dist/src/table/create.action-row.js +135 -0
  78. package/dist/src/table/create.action-row.js.map +1 -0
  79. package/dist/src/table/create.status-cell.js +49 -0
  80. package/dist/src/table/create.status-cell.js.map +1 -0
  81. package/dist/src/table/create.table.js +233 -0
  82. package/dist/src/table/create.table.js.map +1 -0
  83. package/dist/src/table/custom.filter-operators.js +89 -0
  84. package/dist/src/table/custom.filter-operators.js.map +1 -0
  85. package/dist/src/table/dino.js +129 -0
  86. package/dist/src/table/dino.js.map +1 -0
  87. package/dist/src/table/helpers.js +116 -0
  88. package/dist/src/table/helpers.js.map +1 -0
  89. package/dist/src/table/model-filter.js +23 -0
  90. package/dist/src/table/model-filter.js.map +1 -0
  91. package/dist/src/table/toolbar-pannel.js +134 -0
  92. package/dist/src/table/toolbar-pannel.js.map +1 -0
  93. package/dist/src/table/ui.buttons.js +60 -0
  94. package/dist/src/table/ui.buttons.js.map +1 -0
  95. package/dist/src/table/ui.units.js +201 -0
  96. package/dist/src/table/ui.units.js.map +1 -0
  97. package/dist/src/utils/dayjs-config.js +12 -0
  98. package/dist/src/utils/dayjs-config.js.map +1 -0
  99. package/dist/src/utils/helpers.js +197 -0
  100. package/dist/src/utils/helpers.js.map +1 -0
  101. package/dist/src/utils/json-object.js +38 -0
  102. package/dist/src/utils/json-object.js.map +1 -0
  103. package/dist/src/utils/query-param.js +172 -0
  104. package/dist/src/utils/query-param.js.map +1 -0
  105. package/package.json +52 -0
  106. package/rollup.config.js +39 -0
  107. package/src/@types/global.d.ts +5 -0
  108. package/src/api-context/alert-global.tsx +174 -0
  109. package/src/api-context/drawer-global.tsx +116 -0
  110. package/src/api-context/global-modal.tsx +109 -0
  111. package/src/api-context/index.ts +13 -0
  112. package/src/api-context/popover-global.tsx +107 -0
  113. package/src/api-context/popover.tsx +89 -0
  114. package/src/api-context/ui.units.tsx +10 -0
  115. package/src/components/copy-to-clipboard.tsx +86 -0
  116. package/src/components/custom.breadcrumbs.tsx +67 -0
  117. package/src/components/help-tooltip.tsx +75 -0
  118. package/src/components/image-with-fallback.tsx +51 -0
  119. package/src/components/index.tsx +1 -0
  120. package/src/components/input-debounce-timer.tsx +138 -0
  121. package/src/components/loading-buttons.tsx +35 -0
  122. package/src/components/text-editor.preview.tsx +30 -0
  123. package/src/components/text-editor.tsx +125 -0
  124. package/src/form/README.md +55 -0
  125. package/src/form/create.autocomplete.chips.tsx +199 -0
  126. package/src/form/create.date-expired.tsx +195 -0
  127. package/src/form/create.date-picker.tsx +122 -0
  128. package/src/form/create.form-base.tsx +102 -0
  129. package/src/form/create.form-comfirm.tsx +83 -0
  130. package/src/form/create.form-grid-layout.tsx +170 -0
  131. package/src/form/create.form-grid-layout.units.tsx +37 -0
  132. package/src/form/create.input-base.tsx +222 -0
  133. package/src/form/create.input.file.tsx +76 -0
  134. package/src/form/create.select-simple.tsx +101 -0
  135. package/src/form/create.select-with-api.tsx +213 -0
  136. package/src/form/create.text-editor.tsx +161 -0
  137. package/src/form/dino-form.tsx +40 -0
  138. package/src/form/helper.ts +132 -0
  139. package/src/form/index.ts +12 -0
  140. package/src/form/modal-wrapper.tsx +75 -0
  141. package/src/form/types.ts +16 -0
  142. package/src/form/validator.ts +202 -0
  143. package/src/hooks/index.ts +44 -0
  144. package/src/index.ts +7 -0
  145. package/src/lab/create.autocomplete.simple.tsx +57 -0
  146. package/src/lab/create.dino-store.ts +59 -0
  147. package/src/lab/create.multi-select-dropdown.tsx +189 -0
  148. package/src/lab/create.select-mul-with-api/index.tsx +271 -0
  149. package/src/lab/create.select-mul-with-api/table-custom.tsx +194 -0
  150. package/src/lab/create.select-mul-with-api/types.ts +26 -0
  151. package/src/lab/create.select-mul-with-api/ui.units.tsx +163 -0
  152. package/src/lab/filter-bar/base.tsx +162 -0
  153. package/src/lab/filter-bar/create.filter-bar.tsx +190 -0
  154. package/src/lab/filter-bar/create.filter-menu.tsx +156 -0
  155. package/src/lab/filter-bar/create.filter-panel.tsx +95 -0
  156. package/src/lab/filter-bar/create.filtered.tsx +41 -0
  157. package/src/lab/filter-bar/create.sort-menu.tsx +43 -0
  158. package/src/lab/filter-bar/demo.tsx +50 -0
  159. package/src/lab/filter-bar/index.ts +6 -0
  160. package/src/lab/filter-bar/types.ts +105 -0
  161. package/src/lab/filter-bar/ui.units.tsx +70 -0
  162. package/src/lab/grafana-dashboard/configs.ts +43 -0
  163. package/src/lab/grafana-dashboard/date-time-range/absolute-time-rage.tsx +137 -0
  164. package/src/lab/grafana-dashboard/date-time-range/helpers.ts +126 -0
  165. package/src/lab/grafana-dashboard/date-time-range/index.tsx +62 -0
  166. package/src/lab/grafana-dashboard/date-time-range/menu-wrap.tsx +101 -0
  167. package/src/lab/grafana-dashboard/date-time-range/quick-ranges.tsx +161 -0
  168. package/src/lab/grafana-dashboard/date-time-range/types.ts +9 -0
  169. package/src/lab/grafana-dashboard/date-time-range/units.tsx +18 -0
  170. package/src/lab/grafana-dashboard/helper.ts +25 -0
  171. package/src/lab/grafana-dashboard/hooks.tsx +79 -0
  172. package/src/lab/grafana-dashboard/icons.tsx +67 -0
  173. package/src/lab/grafana-dashboard/index.tsx +120 -0
  174. package/src/lab/grafana-dashboard/top-bar.tsx +62 -0
  175. package/src/lab/grafana-dashboard/top-bar.types.ts +5 -0
  176. package/src/lab/grafana-dashboard/types.ts +8 -0
  177. package/src/lab/media-player.core1.tsx +273 -0
  178. package/src/lab/media-player.muted.tsx +62 -0
  179. package/src/lab/media-player.units.ts +80 -0
  180. package/src/lab/table-grid/create.table-grid.tsx +183 -0
  181. package/src/lab/table-grid/demo.tsx +53 -0
  182. package/src/lab/table-grid/dino.tsx +8 -0
  183. package/src/lab/table-grid/helpers.tsx +11 -0
  184. package/src/lab/table-grid/index.ts +3 -0
  185. package/src/lab/table-grid/item-actions.tsx +138 -0
  186. package/src/lab/table-grid/toolbar-pannel.tsx +98 -0
  187. package/src/lab/table-grid/types.ts +68 -0
  188. package/src/redux/create.hoc-lazy.tsx +80 -0
  189. package/src/redux/dino.ts +9 -0
  190. package/src/redux/index.ts +6 -0
  191. package/src/redux/types.ts +27 -0
  192. package/src/redux/ui.error-page.tsx +62 -0
  193. package/src/redux/ui.units.tsx +41 -0
  194. package/src/redux/vector-404.webp +0 -0
  195. package/src/table/context.tsx +16 -0
  196. package/src/table/create.action-row.tsx +91 -0
  197. package/src/table/create.status-cell.tsx +51 -0
  198. package/src/table/create.table.tsx +239 -0
  199. package/src/table/custom.filter-operators.ts +94 -0
  200. package/src/table/dino.tsx +120 -0
  201. package/src/table/helpers.ts +94 -0
  202. package/src/table/index.ts +13 -0
  203. package/src/table/model-filter.ts +43 -0
  204. package/src/table/toolbar-pannel.tsx +106 -0
  205. package/src/table/types.ts +50 -0
  206. package/src/table/ui.buttons.tsx +54 -0
  207. package/src/table/ui.units.tsx +189 -0
  208. package/src/utils/dayjs-config.ts +13 -0
  209. package/src/utils/helpers.ts +171 -0
  210. package/src/utils/index.ts +7 -0
  211. package/src/utils/json-object.ts +29 -0
  212. package/src/utils/mfe-events.tsx +34 -0
  213. package/src/utils/query-param.ts +129 -0
  214. package/tsconfig.json +20 -0
@@ -0,0 +1,194 @@
1
+ import React, { Component, ReactNode } from 'react'
2
+ import { Box, styled, Typography } from '@mui/material'
3
+ import { DataGrid, DataGridProps, GridColDef, GridFeatureMode, GridPaginationModel, GridRowSelectionModel } from '@mui/x-data-grid'
4
+ import { SelectModel } from './types'
5
+
6
+ const tableCustomClasses = {
7
+ root: 'TableCustom-root',
8
+ content: 'TableCustom-content',
9
+ header: 'TableCustom-header',
10
+ contentInner: 'TableCustom-contentInner'
11
+ }
12
+
13
+ interface IProps {
14
+ title: string
15
+ rows: SelectModel[]
16
+ featureMode?: GridFeatureMode
17
+ totalRows?: number
18
+ maxSelected?: number
19
+ pagination?: GridPaginationModel
20
+ defaultPagination?: GridPaginationModel
21
+ loading?: boolean
22
+ onPaginationChange?: DataGridProps['onPaginationModelChange']
23
+ isRowSelectable?: DataGridProps['isRowSelectable']
24
+ slots?: {
25
+ title?: { before?: ReactNode; after?: ReactNode }
26
+ renderAction?: (value: SelectModel) => ReactNode
27
+ renderHeaderAction?: (parmas: { rowSelecteds: GridRowSelectionModel }) => ReactNode
28
+ header?: ReactNode
29
+ topBar?: ReactNode
30
+ dataGridProps?: Partial<DataGridProps>
31
+ }
32
+ }
33
+
34
+ export class TableCustom extends Component<IProps> {
35
+ rowSelecteds: GridRowSelectionModel
36
+ constructor(props: IProps) {
37
+ super(props)
38
+ this.rowSelecteds = []
39
+ }
40
+
41
+ shouldComponentUpdate(nextProps: Readonly<IProps>, nextState: Readonly<{}>, nextContext: any): boolean {
42
+ if (this.props.rows.length !== nextProps.rows.length) this.rowSelecteds = []
43
+ return true
44
+ }
45
+
46
+ render() {
47
+ return (
48
+ <Wrap className={tableCustomClasses.root}>
49
+ <Box className={tableCustomClasses.header}>
50
+ {this.props.slots?.title?.before}
51
+ <Typography variant='subtitle1' gutterBottom sx={{ fontWeight: 700, flex: 1 }}>
52
+ {this.props.title}
53
+ </Typography>
54
+ {this.props.slots?.title?.after}
55
+ </Box>
56
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: '8px', height: '48px', mb: '4px' }}>{this.props.slots?.topBar}</Box>
57
+ <div className={tableCustomClasses.content}>
58
+ <div className={tableCustomClasses.contentInner}>
59
+ <DataGridCustom {...this.getDataGridProps()} {...this.props.slots?.dataGridProps} />
60
+ </div>
61
+ </div>
62
+ </Wrap>
63
+ )
64
+ }
65
+
66
+ getDataGridProps = (): DataGridProps => {
67
+ const columns: GridColDef[] = this.getColumns()
68
+ const data = this.getData()
69
+ let obj: DataGridProps = {
70
+ rows: data.rows,
71
+ columns: columns,
72
+ getRowId: (x) => x.value,
73
+ initialState: { pagination: { paginationModel: this.props.defaultPagination } },
74
+ rowSelectionModel: data.rowSelecteds,
75
+ onRowSelectionModelChange: this.handleRowSelectionChange,
76
+ isRowSelectable: (x) => !x.row.disabled,
77
+ getRowClassName: (x) => (x.row.disabled ? 'Mui-disabled-row' : ''),
78
+ checkboxSelection: true
79
+ }
80
+ if (this.props.featureMode === 'server') {
81
+ obj = {
82
+ ...obj,
83
+ paginationMode: this.props.featureMode,
84
+ loading: this.props.loading,
85
+ rowCount: this.props.totalRows,
86
+ paginationModel: this.props.pagination,
87
+ onPaginationModelChange: this.props.onPaginationChange
88
+ }
89
+ }
90
+ return obj
91
+ }
92
+
93
+ getData = () => {
94
+ let list = [...this.props.rows]
95
+ const rowSelecteds = this.rowSelecteds.filter((x) => list.some((i) => i.value === x.toString() && !i.disabled))
96
+
97
+ if (this.props.maxSelected && rowSelecteds.length >= this.props.maxSelected) {
98
+ const selectedSet = new Set(rowSelecteds)
99
+ list = this.props.rows.map((x) => (selectedSet.has(x.value) ? x : { ...x, disabled: true }))
100
+ }
101
+
102
+ return { rows: list, rowSelecteds }
103
+ }
104
+
105
+ getColumns = (): GridColDef[] => {
106
+ const { slots } = this.props
107
+ const data = this.getData()
108
+ return [
109
+ {
110
+ field: 'value',
111
+ headerName: 'Medias',
112
+ sortable: false,
113
+ disableColumnMenu: true,
114
+ filterable: false,
115
+ flex: 1,
116
+ renderHeader: () => (
117
+ <Box sx={{ display: 'flex', alignItems: 'center', width: '100%', pr: '2px' }}>
118
+ {this.renderHeader()}
119
+ {slots?.renderHeaderAction && slots.renderHeaderAction({ rowSelecteds: data.rowSelecteds })}
120
+ </Box>
121
+ ),
122
+ renderCell: (params: { row: SelectModel }) => (
123
+ <Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
124
+ <Typography variant='body2' noWrap sx={{ flex: 1 }}>
125
+ {params.row.label ?? params.row.value}
126
+ </Typography>
127
+ {this.props.slots?.renderAction && this.props.slots.renderAction(params.row)}
128
+ </Box>
129
+ )
130
+ }
131
+ ]
132
+ }
133
+
134
+ renderHeader = () => {
135
+ const { slots } = this.props
136
+ if (!slots?.header || typeof slots?.header === 'string') {
137
+ return (
138
+ <Typography variant='body2' noWrap sx={{ flex: 1, fontWeight: 700 }}>
139
+ {slots?.header ?? 'Media'}
140
+ </Typography>
141
+ )
142
+ }
143
+ return slots?.header
144
+ }
145
+
146
+ handleRowSelectionChange = (value: GridRowSelectionModel) => {
147
+ if (this.props.maxSelected && value.length > this.props.maxSelected) {
148
+ this.rowSelecteds = value.slice(0, this.props.maxSelected)
149
+ } else {
150
+ this.rowSelecteds = value
151
+ }
152
+ this.forceUpdate()
153
+ }
154
+ }
155
+
156
+ const DataGridCustom = styled(DataGrid)({
157
+ border: 'none',
158
+ '.MuiDataGrid-columnHeaderTitleContainerContent': {
159
+ width: '100%'
160
+ },
161
+ '.Mui-disabled-row': {
162
+ pointerEvents: 'none',
163
+ opacity: 0.4
164
+ },
165
+ '& .MuiDataGrid-cell': {
166
+ borderBottom: 'none'
167
+ }
168
+ })
169
+
170
+ const Wrap = styled(Box)({
171
+ flex: 1,
172
+ paddingTop: '10px',
173
+ height: '100%',
174
+ display: 'flex',
175
+ flexDirection: 'column',
176
+ minHeight: '300px',
177
+ [`.${tableCustomClasses.header}`]: {
178
+ display: 'flex',
179
+ alignItems: 'center'
180
+ },
181
+ [`.${tableCustomClasses.content}`]: {
182
+ flex: 1,
183
+ position: 'relative'
184
+ },
185
+ [`.${tableCustomClasses.contentInner}`]: {
186
+ position: 'absolute',
187
+ top: 0,
188
+ left: 0,
189
+ width: '100%',
190
+ height: '100%'
191
+ }
192
+ })
193
+
194
+ const Custom = styled(Box)(({ theme }) => ({}))
@@ -0,0 +1,26 @@
1
+ import { FilterState } from '../filter-bar'
2
+
3
+ export interface SelectModel<F = any> {
4
+ value: string
5
+ label?: string
6
+ disabled?: boolean
7
+ other?: F
8
+ }
9
+
10
+ export interface PaginationModel {
11
+ /** @default 0 */
12
+ page: number
13
+ /** @default 100 */
14
+ pageSize: number
15
+ }
16
+
17
+ export interface FilterStateCustom<T> extends FilterState<T> {
18
+ pagination: PaginationModel
19
+ }
20
+
21
+ export type SeclectMulFetchData<T> = (
22
+ filterState: FilterStateCustom<T>,
23
+ signal?: AbortSignal
24
+ ) => Promise<{ items: SelectModel<T>[]; totalItems: number }>
25
+
26
+ export type SeclectMulFetchTotal = (signal?: AbortSignal) => Promise<number>
@@ -0,0 +1,163 @@
1
+ import React, { ChangeEvent, FC, useState } from 'react'
2
+ import { IconButton, InputAdornment, InputBase, InputBaseProps } from '@mui/material'
3
+ import { Box, Button, ButtonProps, FormControlLabel, styled, Switch, SwitchProps, Typography } from '@mui/material'
4
+ import EastIcon from '@mui/icons-material/East'
5
+ import ClearIcon from '@mui/icons-material/Clear'
6
+ import CheckIcon from '@mui/icons-material/Check'
7
+ import RemoveIcon from '@mui/icons-material/Remove'
8
+ import SearchIcon from '@mui/icons-material/Search'
9
+ import PlaylistRemoveIcon from '@mui/icons-material/PlaylistRemove'
10
+ import { SelectModel } from './types'
11
+ import { LoadingButton, LoadingButtonProps } from '../../components/loading-buttons'
12
+ import HelpTooltip from '../../components/help-tooltip'
13
+
14
+ export const ButtonAddToRight: FC<ButtonProps> = (props) => (
15
+ <Button size='small' endIcon={<EastIcon fontSize='small' />} sx={{ whiteSpace: 'nowrap', flex: '0 0 auto', textTransform: 'none' }} {...props}>
16
+ Add to Right
17
+ </Button>
18
+ )
19
+
20
+ export const ButtonRemoveSelected: FC<ButtonProps> = (props) => (
21
+ <Button
22
+ size='small'
23
+ endIcon={<PlaylistRemoveIcon fontSize='small' />}
24
+ sx={{ whiteSpace: 'nowrap', flex: '0 0 auto', textTransform: 'none' }}
25
+ {...props}
26
+ >
27
+ Remove Selected
28
+ </Button>
29
+ )
30
+
31
+ export const ButtonAdd: FC<Omit<ButtonProps, 'value'> & { value: SelectModel }> = ({ value, ...props }) => {
32
+ if (value.disabled) {
33
+ return (
34
+ <Box sx={{ padding: '5px' }}>
35
+ <CheckIcon fontSize='small' />
36
+ </Box>
37
+ )
38
+ }
39
+ return (
40
+ <IconButton size='small' {...props}>
41
+ <EastIcon fontSize='small' />
42
+ </IconButton>
43
+ )
44
+ }
45
+
46
+ export const ButtonRemove: FC<ButtonProps> = (props) => (
47
+ <IconButton size='small' {...props}>
48
+ <RemoveIcon fontSize='small' />
49
+ </IconButton>
50
+ )
51
+
52
+ export const ButtonSubmit: FC<LoadingButtonProps> = (props) => (
53
+ <LoadingButton size='small' variant='contained' sx={{ minWidth: '120px' }} {...props}>
54
+ Submit
55
+ </LoadingButton>
56
+ )
57
+
58
+ export const Buttons = {
59
+ AddToRight: ButtonAddToRight,
60
+ RemoveSelected: ButtonRemoveSelected,
61
+ Add: ButtonAdd,
62
+ Remove: ButtonRemove,
63
+ Submit: ButtonSubmit
64
+ }
65
+
66
+ // export const FooterBar: FC<ButtonProps> = (props) => (
67
+ // <Box sx={{ display: 'flex', justifyContent: 'flex-end', padding: '5px 10px 5px' }}>
68
+ // <Button size='small' variant='contained' disabled={props.disabled} onClick={props.onClick}>
69
+ // Confirm Selection
70
+ // </Button>
71
+ // </Box>
72
+ // )
73
+
74
+ export const SwitchUnselected: FC<SwitchProps & { totalHidden?: number }> = ({ totalHidden: hiddenCount, ...props }) => (
75
+ <FormControlLabel
76
+ control={<Switch size='small' {...props} />}
77
+ label={
78
+ <Typography variant='body2'>
79
+ only unselected <Typography variant='caption'> (hidden: {hiddenCount ?? 0})</Typography>
80
+ </Typography>
81
+ }
82
+ />
83
+ )
84
+
85
+ interface InputSearchLocalProps extends Omit<InputBaseProps, 'onChange'> {
86
+ onChange?: (value: string) => void
87
+ }
88
+
89
+ export const InputSearchLocal: FC<InputSearchLocalProps> = ({ onChange, ...props }) => {
90
+ const [value, setValue] = useState('')
91
+
92
+ const searchIncludes = ['Media name']
93
+
94
+ const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
95
+ setValue(event.target.value)
96
+ onChange && onChange(event.target.value)
97
+ }
98
+
99
+ const handleClear = () => {
100
+ setValue('')
101
+ onChange && onChange('')
102
+ }
103
+
104
+ return (
105
+ <InputBaseCustom
106
+ placeholder='Find in selected list'
107
+ fullWidth
108
+ value={value}
109
+ onChange={handleChange}
110
+ startAdornment={
111
+ <InputAdornment position='start'>
112
+ <SearchIcon />
113
+ </InputAdornment>
114
+ }
115
+ endAdornment={
116
+ <InputAdornment position='end'>
117
+ {value && (
118
+ <IconButton size='small' onClick={handleClear}>
119
+ <ClearIcon fontSize='small' />
120
+ </IconButton>
121
+ )}
122
+ <HelpTooltip small title='The search includes'>
123
+ <WrapList>
124
+ {searchIncludes.map((item, index) => (
125
+ <Typography key={index} component='li' variant='body2'>
126
+ {item}
127
+ </Typography>
128
+ ))}
129
+ </WrapList>
130
+ </HelpTooltip>
131
+ </InputAdornment>
132
+ }
133
+ {...props}
134
+ />
135
+ )
136
+ }
137
+
138
+ const InputBaseCustom = styled(InputBase)({
139
+ height: '44px',
140
+ borderRadius: '6px',
141
+ padding: '0 10px',
142
+ transition: 'all linear 0.2s',
143
+ backgroundColor: '#fafafa',
144
+ '&:hover': { backgroundColor: '#ededed' }
145
+ })
146
+
147
+ const WrapList = styled('ul')({
148
+ paddingLeft: '1.7rem',
149
+ marginBottom: 0,
150
+ li: {
151
+ position: 'relative',
152
+ textAlign: 'justify',
153
+ '&::before': {
154
+ content: '"►"',
155
+ display: 'block',
156
+ position: 'absolute',
157
+ top: '50%',
158
+ right: 'calc(100% + 6px)',
159
+ transform: 'translateY(-50%)',
160
+ fontSize: '0.9em'
161
+ }
162
+ }
163
+ })
@@ -0,0 +1,162 @@
1
+ import React, { Component } from 'react'
2
+ import { EFieldValidate, ESearchMatch, FilterField, FilterFields, FilterItemModel, FilterModel, FilterState } from './types'
3
+
4
+ export const filterPanelClasses = {
5
+ root: 'FilterPanel-root',
6
+ list: 'FilterPanel-list',
7
+ item: 'FilterPanel-item'
8
+ }
9
+
10
+ export default class FilterBarBase<P, S, T = any> extends Component<P, S> {
11
+ //#region Filter
12
+ mergeFilter = (f1?: FilterState<T>, f2?: FilterState<T>): FilterState<T> => {
13
+ const mergedFilter: FilterModel<T> = f2?.filter ?? {}
14
+
15
+ if (f1?.filter) {
16
+ for (const key in f1?.filter) {
17
+ const fieldKey = key as keyof T
18
+ const existingItems = mergedFilter[fieldKey] ?? []
19
+ const newItems = f1?.filter[fieldKey] ?? []
20
+
21
+ const existingValues = new Set(existingItems.map((item) => item.value))
22
+
23
+ const mergedItems = [...existingItems, ...newItems.filter((item) => !existingValues.has(item.value))]
24
+
25
+ if (mergedItems.length > 0) {
26
+ mergedFilter[fieldKey] = mergedItems
27
+ }
28
+ }
29
+ }
30
+
31
+ const result: FilterState<T> = {
32
+ filter: Object.keys(mergedFilter).length ? mergedFilter : {},
33
+ details: f2?.details ?? f1?.details
34
+ }
35
+
36
+ return result
37
+ }
38
+
39
+ patchFilterWithKey = (filterState: FilterState<T>, key: keyof FilterModel<T>, value: FilterItemModel) => {
40
+ const clonedFilter: FilterModel<T> = { ...(filterState.filter || {}) }
41
+ let filter = clonedFilter[key] ?? []
42
+ const isChanged = !filter.map((x) => x.value).includes(value.value)
43
+ if (isChanged) filter = [...filter, value]
44
+ clonedFilter[key] = filter
45
+ return { isChanged, filterState: { ...filterState, filter: clonedFilter } }
46
+ }
47
+
48
+ deleteFilterWithKey = (filterState: FilterState<T>, key: keyof FilterModel<T>) => {
49
+ const clonedFilter: FilterModel<T> = { ...(filterState.filter || {}) }
50
+ const isChanged = key in clonedFilter
51
+ if (isChanged) delete clonedFilter[key]
52
+ return { isChanged, filterState: { ...filterState, filter: clonedFilter } }
53
+ }
54
+
55
+ isFilterEmpty = (filterState: FilterState<T>) => {
56
+ const values = Object.values(filterState.filter ?? {})
57
+ return values.length < 1 || values.every((x) => x.length < 1)
58
+ }
59
+ //#endregion
60
+
61
+ //#region Field
62
+ isYoutubeLink = (val: string) => /^https?:\/\/(www\.)?(youtube\.com|youtu\.be)\//.test(val)
63
+
64
+ isGenericLink = (val: string) => /^https?:\/\/[^ ]+$/.test(val)
65
+
66
+ isGuid = (val: string) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(val)
67
+
68
+ getFieldsMatches = (fields: FilterFields<T>, keyword: string, showAll?: boolean): FilterFields<T> => {
69
+ const trimmed = keyword.trim().toLowerCase()
70
+ if (!trimmed) return fields
71
+
72
+ const matched: FilterFields<T> = {}
73
+
74
+ for (const key in fields) {
75
+ const field = key as keyof FilterFields<T>
76
+ const config = fields[field]
77
+ if (!config) continue
78
+
79
+ const matches = Array.isArray(config.searchMatches) ? config.searchMatches : config.searchMatches ? [config.searchMatches] : []
80
+
81
+ let visible = false
82
+
83
+ for (const matchObj of matches) {
84
+ switch (matchObj.rule) {
85
+ case ESearchMatch.AlwaysVisible:
86
+ visible = true
87
+ break
88
+ case ESearchMatch.MatchOnly:
89
+ if (typeof matchObj.match === 'function') {
90
+ if (matchObj.match(trimmed)) visible = true
91
+ }
92
+ break
93
+ case ESearchMatch.LinkYoutube:
94
+ if (this.isYoutubeLink(trimmed)) visible = true
95
+ break
96
+ case ESearchMatch.Link:
97
+ if (this.isGenericLink(trimmed)) visible = true
98
+ break
99
+ case ESearchMatch.Guid:
100
+ if (this.isGuid(trimmed)) visible = true
101
+ break
102
+ default:
103
+ break
104
+ }
105
+
106
+ if (visible) break
107
+ }
108
+
109
+ if (visible) matched[field] = config
110
+ }
111
+ return Object.keys(matched).length === 0 ? (showAll ? fields : {}) : matched
112
+ }
113
+
114
+ validated = (value: string, fieldKey: keyof FilterModel<T>, filterField?: FilterField): { error: boolean; message?: string } => {
115
+ const trimmed = value.trim().toLowerCase()
116
+ if (!trimmed) return { error: true, message: `The ${fieldKey.toString()} is required ` }
117
+ const validate = Array.isArray(filterField?.validate) ? (filterField?.validate ?? []) : filterField?.validate ? [filterField.validate] : []
118
+ const obj: { error: boolean; message?: string } = { error: false }
119
+ for (const validateObj of validate) {
120
+ switch (validateObj.rule) {
121
+ case EFieldValidate.LinkYoutube:
122
+ if (!this.isYoutubeLink(trimmed)) {
123
+ obj.error = true
124
+ obj.message = `The ${filterField?.label ?? fieldKey.toString()} must be a valid YouTube link`
125
+ }
126
+ break
127
+
128
+ case EFieldValidate.Link:
129
+ if (!this.isGenericLink(trimmed)) {
130
+ obj.error = true
131
+ obj.message = `The ${filterField?.label ?? fieldKey.toString()} must be a valid URL link`
132
+ }
133
+ break
134
+
135
+ case EFieldValidate.Custom:
136
+ if (typeof validateObj.custom === 'function' && !validateObj.custom(trimmed)) {
137
+ obj.error = true
138
+ obj.message = `The ${filterField?.label ?? fieldKey.toString()} is invalid`
139
+ }
140
+ break
141
+ default:
142
+ break
143
+ }
144
+ if (obj.error) break
145
+ }
146
+ return obj
147
+ }
148
+ //#endregion
149
+
150
+ //#region Sort
151
+ getSortFields = (fields: FilterFields<T>) => {
152
+ const keys = Object.keys(fields) as (keyof FilterFields<T>)[]
153
+ return keys.reduce<FilterFields<T>>((a, b) => {
154
+ const item = fields[b]
155
+ if (item?.sortable !== false) {
156
+ a[b] = item
157
+ }
158
+ return a
159
+ }, {})
160
+ }
161
+ //#endregion
162
+ }