Compare commits

...

13 Commits

Author SHA1 Message Date
52a97f2a97 fix: update ESLint config to ignore additional directories and files 2026-01-28 21:10:06 +02:00
6c141b71da RELEASING: Releasing 1 package(s)
Releases:
  @warkypublic/oranguru@0.0.30

[skip ci]
2026-01-28 21:05:16 +02:00
89fed20f70 docs(changeset): fix: update GridlerStore setState type to accept full state values 2026-01-28 21:05:11 +02:00
9414421430 fix: update GridlerStore setState type to accept full state values
fix: change defaultItems type in GlidlerFormAdaptor to use MantineBetterMenuInstanceItem[]

test: update global ResizeObserver and IntersectionObserver mocks to use globalThis

build: change moduleResolution to 'bundler' in tsconfig.app.json

build: add missing newline at end of file in tsconfig.node.json
2026-01-28 21:04:51 +02:00
c4f0fcc233 RELEASING: Releasing 1 package(s)
Releases:
  @warkypublic/oranguru@0.0.29

[skip ci]
2026-01-28 20:08:45 +02:00
5180f52698 docs(changeset): feat(Former): update layout to use buttonArea prop instead of buttonOnTop 2026-01-28 20:08:41 +02:00
ce7cf9435a feat(Former): update layout to use buttonArea prop instead of buttonOnTop 2026-01-28 20:07:30 +02:00
Hein
ad2252f5e4 RELEASING: Releasing 1 package(s)
Releases:
  @warkypublic/oranguru@0.0.28

[skip ci]
2026-01-23 11:11:40 +02:00
Hein
287dbcf4da docs(changeset): 1 2026-01-23 11:11:35 +02:00
Hein
f963b38339 fix(Gridler): 🔧 wrap filter value in parentheses 2026-01-23 11:11:12 +02:00
Hein
55cb9038ad RELEASING: Releasing 1 package(s)
Releases:
  @warkypublic/oranguru@0.0.27

[skip ci]
2026-01-23 10:57:58 +02:00
Hein
9d907068a6 docs(changeset): feat(Gridler): add isValuesInPages method and update state handling 2026-01-23 10:57:50 +02:00
Hein
ecb90c69aa feat(Gridler): add isValuesInPages method and update state handling
* Introduce isValuesInPages method to check if values exist in paginated data.
* Update state management in GlidlerAPIAdaptorForGoLangv2 to clear values when no pages are found.
2026-01-23 10:57:32 +02:00
14 changed files with 672 additions and 629 deletions

View File

@@ -1,5 +1,29 @@
# @warkypublic/zustandsyncstore
## 0.0.30
### Patch Changes
- 89fed20: fix: update GridlerStore setState type to accept full state values
## 0.0.29
### Patch Changes
- 5180f52: feat(Former): ✨ update layout to use buttonArea prop instead of buttonOnTop
## 0.0.28
### Patch Changes
- 287dbcf: 1
## 0.0.27
### Patch Changes
- 9d90706: feat(Gridler): ✨ add isValuesInPages method and update state handling
## 0.0.26
### Patch Changes

View File

@@ -11,7 +11,7 @@ const config = defineConfig([
{
extends: ['js/recommended'],
files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
ignores: ['**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', '*stories.tsx','dist/**'],
languageOptions: { globals: globals.browser },
plugins: { js },
},
@@ -20,7 +20,7 @@ const config = defineConfig([
tseslint.configs.recommended,
{
...pluginReact.configs.flat.recommended,
ignores: ['**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', '*stories.tsx'],
ignores: ['**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', '*stories.tsx','dist/**'],
rules: {
...pluginReact.configs.flat.recommended.rules,
'react/react-in-jsx-scope': 'off',
@@ -34,6 +34,7 @@ const config = defineConfig([
'@typescript-eslint/ban-ts-comment': 'off',
},
},
{ignores: ['dist/**','node_modules/**','vite.config.*','eslint.config.*' ]},
]);
export default config;

View File

@@ -1,8 +1,26 @@
{
"name": "@warkypublic/oranguru",
"author": "Warky Devs",
"version": "0.0.26",
"version": "0.0.30",
"type": "module",
"types": "./dist/lib.d.ts",
"main": "./dist/lib.cjs.js",
"module": "./dist/lib.es.js",
"exports": {
".": {
"types": "./dist/lib.d.ts",
"import": "./dist/lib.es.js",
"require": "./dist/lib.cjs.js"
},
"./oranguru.css": "./dist/oranguru.css",
"./package.json": "./package.json"
},
"files": [
"dist/**",
"assets/**",
"public/**",
"global.d.ts"
],
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
@@ -17,72 +35,40 @@
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"files": [
"dist/**",
"assets/**",
"public/**",
"global.d.ts"
],
"module": "./src.lib.ts",
"types": "./src/lib.ts",
"publishConfig": {
"main": "./dist/lib.cjs.js",
"module": "./dist/lib.es.js",
"require": "./dist/lib.cjs.js",
"types": "./dist/lib.d.ts",
"typings": "./dist/lib.d.ts",
"exports": {
".": {
"import": "./dist/lib.es.js",
"types": "./dist/lib.d.ts",
"default": "./dist/lib.cjs.js"
},
"./package.json": "./package.json",
"./oranguru.css": "./dist/oranguru.css"
}
},
"exports": {
".": {
"types": "./src/lib.ts",
"default": "./src/lib.ts"
},
"./oranguru.css": "./src/oranguru.css"
},
"dependencies": {
"@tanstack/react-virtual": "^3.13.18",
"moment": "^2.30.1"
},
"devDependencies": {
"@changesets/cli": "^2.29.7",
"@eslint/js": "^9.38.0",
"@storybook/react": "^10.2.0",
"@storybook/react-vite": "^9.1.15",
"@changesets/cli": "^2.29.8",
"@eslint/js": "^9.39.2",
"@storybook/react-vite": "^10.2.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^24.9.1",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@typescript-eslint/parser": "^8.46.2",
"@vitejs/plugin-react-swc": "^4.2.0",
"@types/node": "^25.1.0",
"@types/react": "^19.2.10",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/parser": "^8.54.0",
"@vitejs/plugin-react-swc": "^4.2.2",
"eslint": "^9.38.0",
"eslint-config-mantine": "^4.0.3",
"eslint-plugin-perfectionist": "^4.15.1",
"eslint-plugin-perfectionist": "^5.4.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-storybook": "^9.1.15",
"global": "^4.4.0",
"globals": "^16.4.0",
"globals": "^17.2.0",
"jiti": "^2.6.1",
"jsdom": "^27.0.1",
"jsdom": "^27.4.0",
"postcss": "^8.5.6",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "^3.6.2",
"prettier-eslint": "^16.4.2",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"storybook": "^9.1.15",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.2",

1102
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,8 +23,8 @@ export interface FormerProps<T extends FieldValues = any> {
id?: string;
keepOpen?: boolean;
layout?: {
buttonArea?: "bottom" | "none" | "top";
buttonAreaGroupProps?: GroupProps;
buttonOnTop?: boolean;
closeButtonProps?: ButtonProps;
closeButtonTitle?: React.ReactNode;
renderBottom?: FormerSectionRender<T>;

View File

@@ -2,8 +2,8 @@ import { useFormerStore } from './Former.store';
import { FormerButtonArea } from './FormerButtonArea';
export const FormerLayoutBottom = () => {
const { buttonOnTop, getState, opened, renderBottom } = useFormerStore((state) => ({
buttonOnTop: state.layout?.buttonOnTop,
const { buttonArea, getState, opened, renderBottom } = useFormerStore((state) => ({
buttonArea: state.layout?.buttonArea,
getState: state.getState,
opened: state.opened,
renderBottom: state.layout?.renderBottom,
@@ -19,5 +19,5 @@ export const FormerLayoutBottom = () => {
);
}
return buttonOnTop ? <></> : <FormerButtonArea />;
return buttonArea === "bottom" ? <FormerButtonArea /> : <></>;
};

View File

@@ -2,8 +2,8 @@ import { useFormerStore } from './Former.store';
import { FormerButtonArea } from './FormerButtonArea';
export const FormerLayoutTop = () => {
const { buttonOnTop, getState, opened, renderTop } = useFormerStore((state) => ({
buttonOnTop: state.layout?.buttonOnTop,
const { buttonArea, getState, opened, renderTop } = useFormerStore((state) => ({
buttonArea: state.layout?.buttonArea,
getState: state.getState,
opened: state.opened,
renderTop: state.layout?.renderTop,
@@ -18,5 +18,5 @@ export const FormerLayoutTop = () => {
getState
);
}
return buttonOnTop ? <FormerButtonArea /> : <></>;
return buttonArea === "top" ? <FormerButtonArea /> : <></>;
};

View File

@@ -35,8 +35,8 @@ export const FormTest = () => {
url: '',
});
const [layout, setLayout] = useState({
buttonArea: "bottom",
buttonAreaGroupProps: { justify: 'center' },
buttonOnTop: false,
title: 'Custom Former Title',
} as FormerProps['layout']);
@@ -63,11 +63,13 @@ export const FormTest = () => {
label="Disable HTML Form"
onChange={(event) => setDisableHTML(event.currentTarget.checked)}
/>
<Switch
checked={layout?.buttonOnTop ?? false}
label="Button On Top"
onChange={(event) => setLayout({ ...layout, buttonOnTop: event.currentTarget.checked })}
<Select
data={['top', 'bottom', 'none']}
onChange={(value) => setLayout({ ...layout, buttonArea: value as 'bottom' | 'none' | 'top' })}
value={layout?.buttonArea}
/>
<Switch
checked={apiOptions.type === 'api'}
label="Use API"

View File

@@ -162,6 +162,7 @@ export interface GridlerState {
hasLocalData: boolean;
isEmpty: boolean;
isValuesInPages: () => boolean
loadingData?: boolean;
loadPage: (page: number, clearMode?: 'all' | 'page') => Promise<void>;
mounted: boolean;
@@ -180,6 +181,7 @@ export interface GridlerState {
onHeaderClicked: (colIndex: number, event: HeaderClickedEventArgs) => void;
onHeaderMenuClick: (col: number, screenPosition: Rectangle) => void;
onItemHovered: (args: GridMouseEventArgs) => void;
onVisibleRegionChanged: (
r: Rectangle,
tx: number,
@@ -189,18 +191,18 @@ export interface GridlerState {
freezeRegions?: readonly Rectangle[];
selected?: Item;
}
) => void;
pageSize: number;
ready: boolean;
refreshCells: (fromRow?: number, toRow?: number, col?: number) => void;
reload?: () => Promise<void>;
reload?: () => Promise<void>;
renderColumns?: GridlerColumns;
setState: <K extends keyof GridlerStoreState>(
key: K,
value: Partial<GridlerStoreState[K]>
value: GridlerStoreState[K]
) => void;
setStateFN: <K extends keyof GridlerStoreState>(
key: K,
@@ -378,6 +380,31 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
},
hasLocalData: false,
isEmpty: true,
isValuesInPages: () => {
const state = get();
if (state.values && Object.keys(state._page_data).length > 0) {
let found = false;
for (const page in state._page_data) {
const pageData = state._page_data[Number(page)];
for (const row of pageData) {
const keyField = state.keyField ?? 'id';
const rowKey = row?.[keyField];
if (rowKey !== undefined) {
const match = state.values.find((v) => String(v?.[keyField]) === String(rowKey));
if (match) {
found = true;
break;
}
}
}
if (found) {
return true;
}
}
}
return false
},
keyField: 'id',
loadPage: async (pPage: number, clearMode?: 'all' | 'page') => {
const state = get();
@@ -511,6 +538,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
return { ...renderCols, [fromItem?.id]: to, [toItem?.id]: from };
});
},
onColumnProposeMove: (startIndex: number, endIndex: number) => {
const s = get();
const fromItem = s.renderColumns?.[startIndex];
@@ -520,7 +548,6 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
}
return true;
},
onColumnResize: (
column: GridColumn,
newSize: number,
@@ -922,7 +949,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
}
},
total_rows: 1000,
uniqueid: getUUID(),
uniqueid: getUUID()
}),
(props) => {
const [setState, getState] = props.useStore((s) => [s.setState, s.getState]);

View File

@@ -230,7 +230,7 @@ function _GlidlerAPIAdaptorForGoLangv2<T = unknown>(props: GlidlerAPIAdaptorForG
ops.push({
name: 'sql_filter',
type: 'custom-sql-w',
value: props.filter,
value: `(${props.filter})`,
});
}
@@ -272,8 +272,12 @@ function _GlidlerAPIAdaptorForGoLangv2<T = unknown>(props: GlidlerAPIAdaptorForG
useEffect(() => {
setState('useAPIQuery', useAPIQuery);
setState('askAPIRowNumber', askAPIRowNumber);
const isValuesInPages = getState('isValuesInPages');
const _refresh = getState('_refresh');
if (!isValuesInPages) {
setState('values', []);
}
//Reset the loaded pages to new rules
_refresh?.().then(() => {
@@ -289,6 +293,8 @@ function _GlidlerAPIAdaptorForGoLangv2<T = unknown>(props: GlidlerAPIAdaptorForG
return <></>;
}
//The computer component does not need to be recalculated on every render, so we use React.memo to prevent unnecessary re-renders.
export const GlidlerAPIAdaptorForGoLangv2 = React.memo(_GlidlerAPIAdaptorForGoLangv2);

View File

@@ -59,8 +59,8 @@ export function GlidlerFormAdaptor(props: {
storeState: GridlerState,
row?: Record<string, unknown>,
col?: GridlerColumn,
defaultItems?: Array<unknown>
) => {
defaultItems?: MantineBetterMenuInstanceItem[]
): MantineBetterMenuInstanceItem[] => {
//console.log('GlidlerFormInterface getMenuItems', id);
if (id === 'header-menu') {

View File

@@ -17,14 +17,14 @@ Object.defineProperty(window, 'matchMedia', {
})
// Mock ResizeObserver
global.ResizeObserver = vi.fn().mockImplementation(() => ({
globalThis.ResizeObserver = vi.fn().mockImplementation(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
unobserve: vi.fn(),
}))
// Mock IntersectionObserver
global.IntersectionObserver = vi.fn().mockImplementation(() => ({
globalThis.IntersectionObserver = vi.fn().mockImplementation(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
unobserve: vi.fn(),

View File

@@ -15,7 +15,7 @@
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Node",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"verbatimModuleSyntax": true,
@@ -37,5 +37,6 @@
"src",
"lib.ts",
"*.d.ts",
]
],
}

View File

@@ -23,5 +23,5 @@
},
"include": [
"vite.config.ts"
]
],
}