@appcorp/fusion-storybook 0.2.0 → 0.2.1

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.
@@ -14,7 +14,7 @@
14
14
  */
15
15
  "use client";
16
16
  import { jsx as _jsx } from "react/jsx-runtime";
17
- import { useMemo } from "react";
17
+ import { useMemo, useCallback } from "react";
18
18
  import { COMPONENT_TYPE } from "@appcorp/shadcn/components/enhanced-table";
19
19
  import { createGenericModulePage, } from "@react-pakistan/util-functions/factory/generic-component-factory";
20
20
  import { useAdmissionModule, AdmissionProvider, ADMISSION_DRAWER, ADMISSION_ACTION_TYPES, } from "./context";
@@ -46,13 +46,15 @@ const tableBodyCols = [
46
46
  // ============================================================================
47
47
  // COMPONENT FACTORY (creates JSX elements when config is created, not during render)
48
48
  // ============================================================================
49
- const createComponentInstances = () => ({
49
+ // Memoized component instances - created once and reused
50
+ const componentInstancesCache = {
50
51
  filter: _jsx(AdmissionFilter, {}),
51
52
  form: _jsx(AdmissionForm, {}),
52
53
  moreActions: _jsx(AdmissionMoreActions, {}),
53
54
  view: _jsx(AdmissionView, {}),
54
- });
55
- const createAdmissionConfig = ({ cancelLabel, dispatch, drawerTitle, labelActions, labelClass, labelEnabled, labelFirstName, labelId, labelLastName, labelRegistrationCode, labelStatus, saveLabel, searchPlaceholder, tableDescription, tableTitle, }) => {
55
+ };
56
+ const createComponentInstances = () => componentInstancesCache;
57
+ const createAdmissionConfig = ({ cancelLabel, drawerTitle, labelActions, labelClass, labelEnabled, labelFirstName, labelId, labelLastName, labelRegistrationCode, labelStatus, saveLabel, searchPlaceholder, tableDescription, tableTitle, onClearFilters, }) => {
56
58
  const components = createComponentInstances();
57
59
  return {
58
60
  moduleName: "admission",
@@ -77,9 +79,7 @@ const createAdmissionConfig = ({ cancelLabel, dispatch, drawerTitle, labelAction
77
79
  tableTitle,
78
80
  viewContent: components.view,
79
81
  size: "small",
80
- onClearFilters: () => {
81
- dispatch({ type: ADMISSION_ACTION_TYPES.RESET_FORM });
82
- },
82
+ onClearFilters,
83
83
  };
84
84
  };
85
85
  // ============================================================================
@@ -88,12 +88,16 @@ const createAdmissionConfig = ({ cancelLabel, dispatch, drawerTitle, labelAction
88
88
  const GenericAdmissionPage = createGenericModulePage();
89
89
  const AdmissionPageInner = (props) => {
90
90
  const context = useAdmissionModule();
91
- // Memoize config creation - INTENTIONALLY EXCLUDE drawer state to prevent infinite loops
92
- // The drawer state changing (from clicking buttons) would recreate this config,
93
- // causing unnecessary re-renders and state updates. Since config should be stable
94
- // across drawer state changes, we exclude it from dependencies.
95
- const admissionConfig = useMemo(() => createAdmissionConfig({
96
- dispatch: context.dispatch,
91
+ // Create a stable onClearFilters callback.
92
+ // dispatch from useReducer is stable across renders, so we can safely use an empty dependency array
93
+ // or only depend on dispatch (which never changes). Using empty array is safest.
94
+ const handleClearFilters = useCallback(() => {
95
+ context.dispatch({ type: ADMISSION_ACTION_TYPES.RESET_FORM });
96
+ }, []);
97
+ // Memoize config creation with translation props only.
98
+ // The key insight: we create the config ONCE when translations change,
99
+ // then apply drawer sizing as a separate, cheaper operation below.
100
+ const baseConfig = useMemo(() => createAdmissionConfig({
97
101
  cancelLabel: props.cancelLabel,
98
102
  drawerTitle: props.drawerTitle,
99
103
  labelActions: props.labelActions,
@@ -108,8 +112,8 @@ const AdmissionPageInner = (props) => {
108
112
  searchPlaceholder: props.searchPlaceholder,
109
113
  tableDescription: props.tableDescription,
110
114
  tableTitle: props.tableTitle,
115
+ onClearFilters: handleClearFilters,
111
116
  }), [
112
- context.dispatch,
113
117
  props.cancelLabel,
114
118
  props.drawerTitle,
115
119
  props.labelActions,
@@ -124,12 +128,24 @@ const AdmissionPageInner = (props) => {
124
128
  props.searchPlaceholder,
125
129
  props.tableDescription,
126
130
  props.tableTitle,
131
+ handleClearFilters,
127
132
  ]);
128
- // Dynamically apply drawer size to config for proper drawer sizing
129
- // This allows size to change without recreating the entire config
130
- const admissionConfigWithSize = useMemo(() => (Object.assign(Object.assign({}, admissionConfig), { size: context.state.drawer === ADMISSION_DRAWER.FORM_DRAWER
131
- ? "full"
132
- : "small" })), [admissionConfig, context.state.drawer]);
133
+ // Apply drawer sizing without recreating the entire config.
134
+ // We use a technique where we only update the size property and pass it separately,
135
+ // ensuring GenericAdmissionPage gets a stable reference unless translation props change.
136
+ const admissionConfig = useMemo(() => {
137
+ const isFormDrawerOpen = context.state.drawer === ADMISSION_DRAWER.FORM_DRAWER;
138
+ // Create a new config only if drawer state actually affects sizing
139
+ // This prevents unnecessary recreations of the same-sized config
140
+ if (isFormDrawerOpen && baseConfig.size === "full") {
141
+ return baseConfig; // Already full size, no need to recreate
142
+ }
143
+ if (!isFormDrawerOpen && baseConfig.size === "small") {
144
+ return baseConfig; // Already small size, no need to recreate
145
+ }
146
+ // Only recreate if size needs to change
147
+ return Object.assign(Object.assign({}, baseConfig), { size: isFormDrawerOpen ? "full" : "small" });
148
+ }, [baseConfig, context.state.drawer]);
133
149
  const hasPermission = resolveRbacPermissions({
134
150
  userRole: props.userRole,
135
151
  moduleName: "Admission",
@@ -137,7 +153,7 @@ const AdmissionPageInner = (props) => {
137
153
  if (!hasPermission) {
138
154
  return _jsx(RbacNoAccess, { moduleName: "Admission" });
139
155
  }
140
- return (_jsx("div", { className: "p-4", children: _jsx(GenericAdmissionPage, { overrideConfig: admissionConfigWithSize, context: context, tableBodyCols: tableBodyCols }) }));
156
+ return (_jsx("div", { className: "p-4", children: _jsx(GenericAdmissionPage, { overrideConfig: admissionConfig, context: context, tableBodyCols: tableBodyCols }) }));
141
157
  };
142
158
  // ============================================================================
143
159
  // PAGE EXPORTS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appcorp/fusion-storybook",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "scripts": {
5
5
  "build-storybook": "storybook build",
6
6
  "build:next": "next build",