Compare commits

...

15 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
Hein
070e56e1af RELEASING: Releasing 1 package(s)
Releases:
  @warkypublic/oranguru@0.0.26

[skip ci]
2026-01-23 10:13:11 +02:00
Hein
3e460ae46c docs(changeset): fixed Gridler selectFirstRow 2026-01-23 10:13:08 +02:00
15 changed files with 705 additions and 595 deletions

View File

@@ -1,5 +1,35 @@
# @warkypublic/zustandsyncstore # @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
- 3e460ae: fixed Gridler selectFirstRow
## 0.0.25 ## 0.0.25
### Patch Changes ### Patch Changes

View File

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

View File

@@ -1,8 +1,26 @@
{ {
"name": "@warkypublic/oranguru", "name": "@warkypublic/oranguru",
"author": "Warky Devs", "author": "Warky Devs",
"version": "0.0.25", "version": "0.0.30",
"type": "module", "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": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
@@ -17,71 +35,40 @@
"storybook": "storybook dev -p 6006", "storybook": "storybook dev -p 6006",
"build-storybook": "storybook build" "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": { "dependencies": {
"@tanstack/react-virtual": "^3.13.18", "@tanstack/react-virtual": "^3.13.18",
"moment": "^2.30.1" "moment": "^2.30.1"
}, },
"devDependencies": { "devDependencies": {
"@changesets/cli": "^2.29.7", "@changesets/cli": "^2.29.8",
"@eslint/js": "^9.38.0", "@eslint/js": "^9.39.2",
"@storybook/react-vite": "^9.1.15", "@storybook/react-vite": "^10.2.1",
"@testing-library/jest-dom": "^6.9.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", "@testing-library/user-event": "^14.6.1",
"@types/node": "^24.9.1", "@types/node": "^25.1.0",
"@types/react": "^19.2.2", "@types/react": "^19.2.10",
"@types/react-dom": "^19.2.2", "@types/react-dom": "^19.2.3",
"@typescript-eslint/parser": "^8.46.2", "@typescript-eslint/parser": "^8.54.0",
"@vitejs/plugin-react-swc": "^4.2.0", "@vitejs/plugin-react-swc": "^4.2.2",
"eslint": "^9.38.0", "eslint": "^9.38.0",
"eslint-config-mantine": "^4.0.3", "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": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24", "eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-storybook": "^9.1.15", "eslint-plugin-storybook": "^9.1.15",
"global": "^4.4.0", "global": "^4.4.0",
"globals": "^16.4.0", "globals": "^17.2.0",
"jiti": "^2.6.1", "jiti": "^2.6.1",
"jsdom": "^27.0.1", "jsdom": "^27.4.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"postcss-preset-mantine": "^1.18.0", "postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1", "postcss-simple-vars": "^7.0.1",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-eslint": "^16.4.2", "prettier-eslint": "^16.4.2",
"react": "^19.2.0", "react": "^19.2.4",
"react-dom": "^19.2.0", "react-dom": "^19.2.4",
"storybook": "^9.1.15", "storybook": "^9.1.15",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"typescript-eslint": "^8.46.2", "typescript-eslint": "^8.46.2",

1095
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
//@ts-ignore
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { useState } from 'react'; import { useState } from 'react';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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