@gravity-ui/dashkit 3.0.2 → 3.1.0-alpha.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.
@@ -22,6 +22,7 @@ interface DashKitDefaultProps {
22
22
  noOverlay?: boolean;
23
23
  draggableHandleClassName?: string;
24
24
  _EXPERIMENTAL_preventDoubleCompact?: boolean;
25
+ _EXPERIMENTAL_memoContext?: boolean;
25
26
  }
26
27
  export interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {
27
28
  }
@@ -67,4 +67,5 @@ DashKit.defaultProps = {
67
67
  },
68
68
  context: {},
69
69
  noOverlay: false,
70
+ _EXPERIMENTAL_memoContext: false,
70
71
  };
@@ -15,9 +15,10 @@ function DashKitView() {
15
15
  return (react_1.default.createElement("div", { className: b({ theme: registerManager.settings.theme }) }, registerManager.settings.isMobile ? (react_1.default.createElement(MobileLayout_1.default, null)) : (react_1.default.createElement(GridLayout_1.default, { ref: forwardedMetaRef }))));
16
16
  }
17
17
  const DashKitViewWithContext = (0, withContext_1.withContext)(DashKitView);
18
+ const DashKitViewWithMemoContext = (0, withContext_1.withMemoContext)(DashKitView);
18
19
  const DashKitViewForwardedMeta = react_1.default.forwardRef((props, ref) => {
19
20
  const layout = (0, useCalcLayout_1.useCalcPropsLayout)(props.config, props.registerManager);
20
- return react_1.default.createElement(DashKitViewWithContext, Object.assign({}, props, { layout: layout, forwardedMetaRef: ref }));
21
+ return props._EXPERIMENTAL_memoContext ? (react_1.default.createElement(DashKitViewWithMemoContext, Object.assign({}, props, { layout: layout, forwardedMetaRef: ref }))) : (react_1.default.createElement(DashKitViewWithContext, Object.assign({}, props, { layout: layout, forwardedMetaRef: ref })));
21
22
  });
22
23
  DashKitViewForwardedMeta.displayName = 'DashKitViewForwardedMeta';
23
24
  exports.default = DashKitViewForwardedMeta;
@@ -163,3 +163,7 @@ export function withContext(Component: any): {
163
163
  };
164
164
  contextType?: React.Context<any> | undefined;
165
165
  };
166
+ export function withMemoContext(Component: any): {
167
+ (props: any): JSX.Element;
168
+ displayName: string;
169
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withContext = void 0;
3
+ exports.withMemoContext = exports.withContext = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const DashKitContext_1 = require("../context/DashKitContext");
@@ -201,3 +201,177 @@ function withContext(Component) {
201
201
  _a;
202
202
  }
203
203
  exports.withContext = withContext;
204
+ function useMemoStateContext(props) {
205
+ // так как мы не хотим хранить параметры виджета с активированной автовысотой в сторе и на сервере, актуальный
206
+ // (видимый юзером в конкретный момент времени) лэйаут (массив объектов с данными о ширине, высоте,
207
+ // расположении конкретного виджета на сетке) будет храниться в стейте, но, для того, чтобы в стор попадал
208
+ // лэйаут без учета вижетов с активированной автовысотой, в момент "подстройки" высоты виджета значение h
209
+ // (высота) из конфига будет запоминаться в originalLayouts, новое значение высоты в adjustedLayouts
210
+ const originalLayouts = react_1.default.useRef({});
211
+ const adjustedLayouts = react_1.default.useRef({});
212
+ // TODO: need move originalLayouts, adjustedLayouts to state
213
+ const [layoutUpdateCounter, forceUpdateLayout] = react_1.default.useState(0);
214
+ const onChange = react_1.default.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams }) => {
215
+ if (!((0, isEqual_1.default)(config, props.config) &&
216
+ (0, isEqual_1.default)(itemsStateAndParams, props.itemsStateAndParams))) {
217
+ props.onChange({ config, itemsStateAndParams });
218
+ }
219
+ }, [props.config, props.itemsStateAndParams, props.onChange]);
220
+ // каллбэк вызывающийся при изменение лэйаута сетки, первым аргументом приходит актуальный конфиг лэйаута,
221
+ // т.е. если на текущей сетке есть виджеты, с активированной опцией автовысоты, их параметр "h" будет
222
+ // "подстроенный"; чтобы, для сохранения в сторе "ушли" значения без учёта подстройки (как если бы у этих
223
+ // виджетов автовысота была деактивирована) корректируем их используя this.originalLayouts
224
+ const onLayoutChange = react_1.default.useCallback((layout) => {
225
+ const currentInnerLayout = layout.map((item) => {
226
+ if (item.i in originalLayouts.current) {
227
+ return Object.assign(Object.assign({}, originalLayouts.current[item.i]), { w: item.w, x: item.x, y: item.y });
228
+ }
229
+ else {
230
+ return Object.assign({}, item);
231
+ }
232
+ });
233
+ // [experimental] if the _prevent Double Compact is enabled, then skip recalculate layout with auto height
234
+ // после того, как у виждета активировали автовысоту и его параметр "h" изменился, это приведёт, также, и к
235
+ // изменению параметра (координаты) "y" у элементов расположенных под ним, поэтому, после того, как
236
+ // значения параметра "h" виджетов с активированной автовысотой были изменены, необходимо изменить и
237
+ // координату "y" виджетов расположенных ниже
238
+ const compactedLayout = props._EXPERIMENTAL_preventDoubleCompact
239
+ ? currentInnerLayout
240
+ : react_grid_layout_1.utils.compact(currentInnerLayout, LAYOUT_COMPACTING_TYPE, props.registerManager.gridLayout.cols);
241
+ const newConfig = utils_1.UpdateManager.updateLayout({
242
+ layout: compactedLayout,
243
+ config: props.config,
244
+ });
245
+ if (!(0, isEqual_1.default)(newConfig.layout, props.config.layout)) {
246
+ onChange({ config: newConfig });
247
+ }
248
+ }, [props._EXPERIMENTAL_preventDoubleCompact, props.registerManager, props.config, onChange]);
249
+ const onItemRemove = react_1.default.useCallback((id) => {
250
+ onChange(utils_1.UpdateManager.removeItem({
251
+ id,
252
+ config: props.config,
253
+ itemsStateAndParams: props.itemsStateAndParams,
254
+ }));
255
+ }, [props.config, props.itemsStateAndParams, onChange]);
256
+ const onItemStateAndParamsChange = react_1.default.useCallback((id, stateAndParams) => {
257
+ onChange({
258
+ itemsStateAndParams: utils_1.UpdateManager.changeStateAndParams({
259
+ id,
260
+ config: props.config,
261
+ stateAndParams,
262
+ itemsStateAndParams: props.itemsStateAndParams,
263
+ }),
264
+ });
265
+ }, [props.config, props.itemsStateAndParams, onChange]);
266
+ const memorizeOriginalLayout = react_1.default.useCallback((widgetId, preAutoHeightLayout, postAutoHeightLayout) => {
267
+ if (!(widgetId in originalLayouts.current)) {
268
+ originalLayouts.current[widgetId] = preAutoHeightLayout;
269
+ }
270
+ adjustedLayouts.current[widgetId] = postAutoHeightLayout;
271
+ forceUpdateLayout((prev) => prev + 1);
272
+ }, []);
273
+ const revertToOriginalLayout = react_1.default.useCallback((widgetId) => {
274
+ delete adjustedLayouts.current[widgetId];
275
+ delete originalLayouts.current[widgetId];
276
+ forceUpdateLayout((prev) => prev + 1);
277
+ }, []);
278
+ const itemsParams = react_1.default.useMemo(() => (0, shared_1.getItemsParams)({
279
+ defaultGlobalParams: props.defaultGlobalParams,
280
+ globalParams: props.globalParams,
281
+ config: props.config,
282
+ itemsStateAndParams: props.itemsStateAndParams,
283
+ plugins: props.registerManager.getPlugins(),
284
+ }), [
285
+ props.defaultGlobalParams,
286
+ props.globalParams,
287
+ props.config,
288
+ props.itemsStateAndParams,
289
+ props.registerManager,
290
+ ]);
291
+ const itemsState = react_1.default.useMemo(() => (0, shared_1.getItemsState)({
292
+ config: props.config,
293
+ itemsStateAndParams: props.itemsStateAndParams,
294
+ }), [props.config, props.itemsStateAndParams]);
295
+ const getItemsMeta = react_1.default.useCallback((pluginsRefs) => {
296
+ return pluginsRefs
297
+ .map((ref) => {
298
+ if (!(ref && typeof ref.getMeta === 'function')) {
299
+ return undefined;
300
+ }
301
+ return ref.getMeta();
302
+ })
303
+ .filter(Boolean);
304
+ }, []);
305
+ const resultLayout = react_1.default.useMemo(() => {
306
+ return props.layout.map((item) => {
307
+ if (item.i in adjustedLayouts.current) {
308
+ return Object.assign(Object.assign({}, adjustedLayouts.current[item.i]), { w: item.w, x: item.x, y: item.y });
309
+ }
310
+ else {
311
+ return Object.assign({}, item);
312
+ }
313
+ });
314
+ }, [props.layout, layoutUpdateCounter]);
315
+ // TODO: don't use getLayout when finished experiment _EXPERIMENTAL_memoContext
316
+ // need use resultLayout like layout props
317
+ const getLayout = react_1.default.useCallback(() => resultLayout, [resultLayout]);
318
+ const reloadItems = react_1.default.useCallback((pluginsRefs, data) => {
319
+ pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
320
+ }, []);
321
+ return react_1.default.useMemo(() => ({
322
+ getLayout,
323
+ config: props.config,
324
+ context: props.context,
325
+ overlayControls: props.overlayControls,
326
+ noOverlay: props.noOverlay,
327
+ defaultGlobalParams: props.globalParams,
328
+ globalParams: props.globalParams,
329
+ editMode: props.editMode,
330
+ settings: props.settings,
331
+ itemsState,
332
+ itemsParams,
333
+ registerManager: props.registerManager,
334
+ onItemStateAndParamsChange,
335
+ removeItem: onItemRemove,
336
+ editItem: props.onItemEdit,
337
+ layoutChange: onLayoutChange,
338
+ getItemsMeta,
339
+ reloadItems,
340
+ memorizeOriginalLayout,
341
+ revertToOriginalLayout,
342
+ forwardedMetaRef: props.forwardedMetaRef,
343
+ draggableHandleClassName: props.draggableHandleClassName,
344
+ }), [
345
+ getLayout,
346
+ props.config,
347
+ props.context,
348
+ props.overlayControls,
349
+ props.noOverlay,
350
+ props.globalParams,
351
+ props.editMode,
352
+ props.settings,
353
+ itemsState,
354
+ itemsParams,
355
+ props.registerManager,
356
+ onItemStateAndParamsChange,
357
+ onItemRemove,
358
+ props.onItemEdit,
359
+ onLayoutChange,
360
+ getItemsMeta,
361
+ reloadItems,
362
+ memorizeOriginalLayout,
363
+ revertToOriginalLayout,
364
+ props.forwardedMetaRef,
365
+ props.draggableHandleClassName,
366
+ ]);
367
+ }
368
+ function withMemoContext(Component) {
369
+ const WithMemoContext = (props) => {
370
+ const contextValue = useMemoStateContext(props);
371
+ return (react_1.default.createElement(DashKitContext_1.DashKitContext.Provider, { value: contextValue },
372
+ react_1.default.createElement(Component, null)));
373
+ };
374
+ WithMemoContext.displayName = `withMemoContext(${Component.displayName || Component.name || 'Component'}`;
375
+ return WithMemoContext;
376
+ }
377
+ exports.withMemoContext = withMemoContext;
@@ -22,6 +22,7 @@ interface DashKitDefaultProps {
22
22
  noOverlay?: boolean;
23
23
  draggableHandleClassName?: string;
24
24
  _EXPERIMENTAL_preventDoubleCompact?: boolean;
25
+ _EXPERIMENTAL_memoContext?: boolean;
25
26
  }
26
27
  export interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {
27
28
  }
@@ -62,4 +62,5 @@ DashKit.defaultProps = {
62
62
  },
63
63
  context: {},
64
64
  noOverlay: false,
65
+ _EXPERIMENTAL_memoContext: false,
65
66
  };
@@ -3,7 +3,7 @@ import block from 'bem-cn-lite';
3
3
  import { DashKitContext } from '../../context/DashKitContext';
4
4
  import GridLayout from '../GridLayout/GridLayout';
5
5
  import MobileLayout from '../MobileLayout/MobileLayout';
6
- import { withContext } from '../../hocs/withContext';
6
+ import { withContext, withMemoContext } from '../../hocs/withContext';
7
7
  import { useCalcPropsLayout } from '../../hooks/useCalcLayout';
8
8
  import './DashKitView.css';
9
9
  const b = block('dashkit');
@@ -13,9 +13,10 @@ function DashKitView() {
13
13
  return (React.createElement("div", { className: b({ theme: registerManager.settings.theme }) }, registerManager.settings.isMobile ? (React.createElement(MobileLayout, null)) : (React.createElement(GridLayout, { ref: forwardedMetaRef }))));
14
14
  }
15
15
  const DashKitViewWithContext = withContext(DashKitView);
16
+ const DashKitViewWithMemoContext = withMemoContext(DashKitView);
16
17
  const DashKitViewForwardedMeta = React.forwardRef((props, ref) => {
17
18
  const layout = useCalcPropsLayout(props.config, props.registerManager);
18
- return React.createElement(DashKitViewWithContext, Object.assign({}, props, { layout: layout, forwardedMetaRef: ref }));
19
+ return props._EXPERIMENTAL_memoContext ? (React.createElement(DashKitViewWithMemoContext, Object.assign({}, props, { layout: layout, forwardedMetaRef: ref }))) : (React.createElement(DashKitViewWithContext, Object.assign({}, props, { layout: layout, forwardedMetaRef: ref })));
19
20
  });
20
21
  DashKitViewForwardedMeta.displayName = 'DashKitViewForwardedMeta';
21
22
  export default DashKitViewForwardedMeta;
@@ -163,3 +163,7 @@ export function withContext(Component: any): {
163
163
  };
164
164
  contextType?: React.Context<any> | undefined;
165
165
  };
166
+ export function withMemoContext(Component: any): {
167
+ (props: any): JSX.Element;
168
+ displayName: string;
169
+ };
@@ -196,3 +196,176 @@ export function withContext(Component) {
196
196
  },
197
197
  _a;
198
198
  }
199
+ function useMemoStateContext(props) {
200
+ // так как мы не хотим хранить параметры виджета с активированной автовысотой в сторе и на сервере, актуальный
201
+ // (видимый юзером в конкретный момент времени) лэйаут (массив объектов с данными о ширине, высоте,
202
+ // расположении конкретного виджета на сетке) будет храниться в стейте, но, для того, чтобы в стор попадал
203
+ // лэйаут без учета вижетов с активированной автовысотой, в момент "подстройки" высоты виджета значение h
204
+ // (высота) из конфига будет запоминаться в originalLayouts, новое значение высоты в adjustedLayouts
205
+ const originalLayouts = React.useRef({});
206
+ const adjustedLayouts = React.useRef({});
207
+ // TODO: need move originalLayouts, adjustedLayouts to state
208
+ const [layoutUpdateCounter, forceUpdateLayout] = React.useState(0);
209
+ const onChange = React.useCallback(({ config = props.config, itemsStateAndParams = props.itemsStateAndParams }) => {
210
+ if (!(isEqual(config, props.config) &&
211
+ isEqual(itemsStateAndParams, props.itemsStateAndParams))) {
212
+ props.onChange({ config, itemsStateAndParams });
213
+ }
214
+ }, [props.config, props.itemsStateAndParams, props.onChange]);
215
+ // каллбэк вызывающийся при изменение лэйаута сетки, первым аргументом приходит актуальный конфиг лэйаута,
216
+ // т.е. если на текущей сетке есть виджеты, с активированной опцией автовысоты, их параметр "h" будет
217
+ // "подстроенный"; чтобы, для сохранения в сторе "ушли" значения без учёта подстройки (как если бы у этих
218
+ // виджетов автовысота была деактивирована) корректируем их используя this.originalLayouts
219
+ const onLayoutChange = React.useCallback((layout) => {
220
+ const currentInnerLayout = layout.map((item) => {
221
+ if (item.i in originalLayouts.current) {
222
+ return Object.assign(Object.assign({}, originalLayouts.current[item.i]), { w: item.w, x: item.x, y: item.y });
223
+ }
224
+ else {
225
+ return Object.assign({}, item);
226
+ }
227
+ });
228
+ // [experimental] if the _prevent Double Compact is enabled, then skip recalculate layout with auto height
229
+ // после того, как у виждета активировали автовысоту и его параметр "h" изменился, это приведёт, также, и к
230
+ // изменению параметра (координаты) "y" у элементов расположенных под ним, поэтому, после того, как
231
+ // значения параметра "h" виджетов с активированной автовысотой были изменены, необходимо изменить и
232
+ // координату "y" виджетов расположенных ниже
233
+ const compactedLayout = props._EXPERIMENTAL_preventDoubleCompact
234
+ ? currentInnerLayout
235
+ : gridLayoutUtils.compact(currentInnerLayout, LAYOUT_COMPACTING_TYPE, props.registerManager.gridLayout.cols);
236
+ const newConfig = UpdateManager.updateLayout({
237
+ layout: compactedLayout,
238
+ config: props.config,
239
+ });
240
+ if (!isEqual(newConfig.layout, props.config.layout)) {
241
+ onChange({ config: newConfig });
242
+ }
243
+ }, [props._EXPERIMENTAL_preventDoubleCompact, props.registerManager, props.config, onChange]);
244
+ const onItemRemove = React.useCallback((id) => {
245
+ onChange(UpdateManager.removeItem({
246
+ id,
247
+ config: props.config,
248
+ itemsStateAndParams: props.itemsStateAndParams,
249
+ }));
250
+ }, [props.config, props.itemsStateAndParams, onChange]);
251
+ const onItemStateAndParamsChange = React.useCallback((id, stateAndParams) => {
252
+ onChange({
253
+ itemsStateAndParams: UpdateManager.changeStateAndParams({
254
+ id,
255
+ config: props.config,
256
+ stateAndParams,
257
+ itemsStateAndParams: props.itemsStateAndParams,
258
+ }),
259
+ });
260
+ }, [props.config, props.itemsStateAndParams, onChange]);
261
+ const memorizeOriginalLayout = React.useCallback((widgetId, preAutoHeightLayout, postAutoHeightLayout) => {
262
+ if (!(widgetId in originalLayouts.current)) {
263
+ originalLayouts.current[widgetId] = preAutoHeightLayout;
264
+ }
265
+ adjustedLayouts.current[widgetId] = postAutoHeightLayout;
266
+ forceUpdateLayout((prev) => prev + 1);
267
+ }, []);
268
+ const revertToOriginalLayout = React.useCallback((widgetId) => {
269
+ delete adjustedLayouts.current[widgetId];
270
+ delete originalLayouts.current[widgetId];
271
+ forceUpdateLayout((prev) => prev + 1);
272
+ }, []);
273
+ const itemsParams = React.useMemo(() => getItemsParams({
274
+ defaultGlobalParams: props.defaultGlobalParams,
275
+ globalParams: props.globalParams,
276
+ config: props.config,
277
+ itemsStateAndParams: props.itemsStateAndParams,
278
+ plugins: props.registerManager.getPlugins(),
279
+ }), [
280
+ props.defaultGlobalParams,
281
+ props.globalParams,
282
+ props.config,
283
+ props.itemsStateAndParams,
284
+ props.registerManager,
285
+ ]);
286
+ const itemsState = React.useMemo(() => getItemsState({
287
+ config: props.config,
288
+ itemsStateAndParams: props.itemsStateAndParams,
289
+ }), [props.config, props.itemsStateAndParams]);
290
+ const getItemsMeta = React.useCallback((pluginsRefs) => {
291
+ return pluginsRefs
292
+ .map((ref) => {
293
+ if (!(ref && typeof ref.getMeta === 'function')) {
294
+ return undefined;
295
+ }
296
+ return ref.getMeta();
297
+ })
298
+ .filter(Boolean);
299
+ }, []);
300
+ const resultLayout = React.useMemo(() => {
301
+ return props.layout.map((item) => {
302
+ if (item.i in adjustedLayouts.current) {
303
+ return Object.assign(Object.assign({}, adjustedLayouts.current[item.i]), { w: item.w, x: item.x, y: item.y });
304
+ }
305
+ else {
306
+ return Object.assign({}, item);
307
+ }
308
+ });
309
+ }, [props.layout, layoutUpdateCounter]);
310
+ // TODO: don't use getLayout when finished experiment _EXPERIMENTAL_memoContext
311
+ // need use resultLayout like layout props
312
+ const getLayout = React.useCallback(() => resultLayout, [resultLayout]);
313
+ const reloadItems = React.useCallback((pluginsRefs, data) => {
314
+ pluginsRefs.forEach((ref) => ref && ref.reload && ref.reload(data));
315
+ }, []);
316
+ return React.useMemo(() => ({
317
+ getLayout,
318
+ config: props.config,
319
+ context: props.context,
320
+ overlayControls: props.overlayControls,
321
+ noOverlay: props.noOverlay,
322
+ defaultGlobalParams: props.globalParams,
323
+ globalParams: props.globalParams,
324
+ editMode: props.editMode,
325
+ settings: props.settings,
326
+ itemsState,
327
+ itemsParams,
328
+ registerManager: props.registerManager,
329
+ onItemStateAndParamsChange,
330
+ removeItem: onItemRemove,
331
+ editItem: props.onItemEdit,
332
+ layoutChange: onLayoutChange,
333
+ getItemsMeta,
334
+ reloadItems,
335
+ memorizeOriginalLayout,
336
+ revertToOriginalLayout,
337
+ forwardedMetaRef: props.forwardedMetaRef,
338
+ draggableHandleClassName: props.draggableHandleClassName,
339
+ }), [
340
+ getLayout,
341
+ props.config,
342
+ props.context,
343
+ props.overlayControls,
344
+ props.noOverlay,
345
+ props.globalParams,
346
+ props.editMode,
347
+ props.settings,
348
+ itemsState,
349
+ itemsParams,
350
+ props.registerManager,
351
+ onItemStateAndParamsChange,
352
+ onItemRemove,
353
+ props.onItemEdit,
354
+ onLayoutChange,
355
+ getItemsMeta,
356
+ reloadItems,
357
+ memorizeOriginalLayout,
358
+ revertToOriginalLayout,
359
+ props.forwardedMetaRef,
360
+ props.draggableHandleClassName,
361
+ ]);
362
+ }
363
+ export function withMemoContext(Component) {
364
+ const WithMemoContext = (props) => {
365
+ const contextValue = useMemoStateContext(props);
366
+ return (React.createElement(DashKitContext.Provider, { value: contextValue },
367
+ React.createElement(Component, null)));
368
+ };
369
+ WithMemoContext.displayName = `withMemoContext(${Component.displayName || Component.name || 'Component'}`;
370
+ return WithMemoContext;
371
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dashkit",
3
- "version": "3.0.2",
3
+ "version": "3.1.0-alpha.0",
4
4
  "description": "Library for rendering dashboard grid layout",
5
5
  "license": "MIT",
6
6
  "repository": {