@donotdev/ui 0.0.15 → 0.0.16

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 (30) hide show
  1. package/dist/components/common/TechBento.d.ts.map +1 -1
  2. package/dist/components/common/TechBento.js +1 -1
  3. package/dist/components/layout/components/header/HeaderNavigation.d.ts.map +1 -1
  4. package/dist/components/layout/components/header/HeaderNavigation.js +2 -2
  5. package/dist/crud/components/CrudCardLink.d.ts.map +1 -1
  6. package/dist/crud/components/EntityCardList.d.ts.map +1 -1
  7. package/dist/crud/components/EntityCardList.js +8 -22
  8. package/dist/crud/components/EntityList.d.ts.map +1 -1
  9. package/dist/crud/components/EntityList.js +7 -43
  10. package/dist/dndev.css +86 -63
  11. package/dist/index.js +4 -4
  12. package/dist/internal/devtools/components/DesignTab.d.ts.map +1 -1
  13. package/dist/internal/layout/components/AutoMetaTags.d.ts.map +1 -1
  14. package/dist/internal/layout/components/AutoMetaTags.js +2 -9
  15. package/dist/internal/layout/components/FontPreloadLinks.d.ts.map +1 -1
  16. package/dist/internal/layout/components/FontPreloadLinks.js +1 -0
  17. package/dist/internal/layout/components/footer/useLegalLinks.d.ts.map +1 -1
  18. package/dist/routing/GoToInput.d.ts.map +1 -1
  19. package/dist/routing/GoToInput.js +2 -1
  20. package/dist/routing/Link.d.ts.map +1 -1
  21. package/dist/routing/useRouteDiscovery.d.ts.map +1 -1
  22. package/dist/styles/index.css +86 -63
  23. package/dist/utils/sanitizeSvg.d.ts.map +1 -1
  24. package/dist/utils/useFormStoreSafe.d.ts.map +1 -1
  25. package/dist/vite-routing/AppRoutes.d.ts.map +1 -1
  26. package/dist/vite-routing/AppRoutes.js +1 -1
  27. package/package.json +1 -1
  28. package/dist/routing/hooks/useFormNavigationBlocker.d.ts +0 -14
  29. package/dist/routing/hooks/useFormNavigationBlocker.d.ts.map +0 -1
  30. package/dist/routing/hooks/useFormNavigationBlocker.js +0 -42
@@ -1 +1 @@
1
- {"version":3,"file":"TechBento.d.ts","sourceRoot":"","sources":["../../../src/components/common/TechBento.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,cAAc,EACd,IAAI,EACL,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAK/D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,OAAO,CAAC;QACd,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;QAC3C,OAAO,CAAC,EAAE,WAAW,CAAC;KACvB,CAAC,CAAC;IACH;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwCF,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,KAAK,EACL,IAAQ,EACR,OAAO,EACP,GAAc,EACd,SAAiB,EACjB,IAAiB,EACjB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,SAAS,GACV,EAAE,cAAc,2CA4DhB;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"TechBento.d.ts","sourceRoot":"","sources":["../../../src/components/common/TechBento.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,cAAc,EACd,IAAI,EACL,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAK/D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,OAAO,CAAC;QACd,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;QAC3C,OAAO,CAAC,EAAE,WAAW,CAAC;KACvB,CAAC,CAAC;IACH;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA0CF,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,KAAK,EACL,IAAQ,EACR,OAAO,EACP,GAAc,EACd,SAAiB,EACjB,IAAiB,EACjB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,SAAS,GACV,EAAE,cAAc,2CA4DhB;AAED,eAAe,SAAS,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // packages/ui/src/components/common/TechBento.tsx
3
3
  import { useState } from 'react';
4
- import { Bento, Card, Section, Stack, Text, TONE, cn } from '@donotdev/components';
4
+ import { Bento, Card, Section, Stack, Text, TONE, cn, } from '@donotdev/components';
5
5
  import { techLogos } from '../../data/techLogos';
6
6
  import { sanitizeSvg } from '../../utils/sanitizeSvg';
7
7
  function TechCard({ techKey, variant, }) {
@@ -1 +1 @@
1
- {"version":3,"file":"HeaderNavigation.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/components/header/HeaderNavigation.tsx"],"names":[],"mappings":"AAsBA,OAAO,EACL,OAAO,EAIR,MAAM,sBAAsB,CAAC;AAU9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IACpC,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,qBAAqB,CAwE1D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"HeaderNavigation.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/components/header/HeaderNavigation.tsx"],"names":[],"mappings":"AAsBA,OAAO,EACL,OAAO,EAIR,MAAM,sBAAsB,CAAC;AAa9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IACpC,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,qBAAqB,CA4E1D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -24,7 +24,7 @@ import { Link } from '../../../../routing/Link';
24
24
  import { Icon } from '../../../common/icon';
25
25
  import { DnDevNavigationMenu } from '../../../../routing/DnDevNavigationMenu';
26
26
  import { NavigationItemComponent } from '../../../../routing/NavigationItem';
27
- import { useNavigationItems, useNavigationRoute } from '../../../../routing/useNavigation';
27
+ import { useNavigationItems, useNavigationRoute, } from '../../../../routing/useNavigation';
28
28
  /**
29
29
  * HeaderNavigation - DISPLAY-aware adaptive navigation component
30
30
  *
@@ -47,7 +47,7 @@ const HeaderNavigation = ({ className = '', showIcons = true, display = DISPLAY.
47
47
  if (path) {
48
48
  if (!singleRoute)
49
49
  return null;
50
- return (_jsx(Button, { variant: "ghost", display: display, icon: showIcons ? _jsx(Icon, { icon: singleRoute.icon, fallback: LinkIcon }) : undefined, className: className, render: ({ children, ...props }) => (_jsx(Link, { path: singleRoute.path, ...props, children: children })), children: singleRoute.label }));
50
+ return (_jsx(Button, { variant: "ghost", display: display, icon: showIcons ? (_jsx(Icon, { icon: singleRoute.icon, fallback: LinkIcon })) : undefined, className: className, render: ({ children, ...props }) => (_jsx(Link, { path: singleRoute.path, ...props, children: children })), children: singleRoute.label }));
51
51
  }
52
52
  const effectiveDisplay = display === DISPLAY.AUTO
53
53
  ? isMobile || isTablet
@@ -1 +1 @@
1
- {"version":3,"file":"CrudCardLink.d.ts","sourceRoot":"","sources":["../../../src/crud/components/CrudCardLink.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,UAAU,EACV,OAAO,EACP,GAAG,IAAI,EACR,EAAE,aAAa,2CAqBf;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"CrudCardLink.d.ts","sourceRoot":"","sources":["../../../src/crud/components/CrudCardLink.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,2CAkBvE;AAED,eAAe,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"EntityCardList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityCardList.tsx"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,mBAAmB,EAAgB,MAAM,gBAAgB,CAAC;AAIxE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAmB,EACnB,SAA0B,EAAE,2BAA2B;AACvD,MAAM,EACN,WAAmB,EACnB,WAAW,EACX,IAAI,GACL,EAAE,mBAAmB,2CA4MrB"}
1
+ {"version":3,"file":"EntityCardList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityCardList.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,mBAAmB,EAAgB,MAAM,gBAAgB,CAAC;AAIxE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAmB,EACnB,SAA0B,EAAE,2BAA2B;AACvD,MAAM,EACN,WAAmB,EACnB,WAAW,EACX,IAAI,GACL,EAAE,mBAAmB,2CA2LrB"}
@@ -9,7 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
9
9
  * **Routing:** Convention basePath = `/${collection}`. View = basePath/:id.
10
10
  * Override basePath for nested routes; use onClick(id) to open sheet instead of navigating.
11
11
  *
12
- * @version 0.2.0
12
+ * @version 0.3.0
13
13
  * @since 0.0.1
14
14
  * @author AMBROISE PARK Consulting
15
15
  */
@@ -18,7 +18,7 @@ import { Heart } from 'lucide-react';
18
18
  import { Card, Grid, Stack, Text, Spinner, Section, Button, } from '@donotdev/components';
19
19
  import { useTranslation, getListCardFieldNames } from '@donotdev/core';
20
20
  import { useNavigate } from '../../routing';
21
- import { useCrudCardList, EntityFilters, useEntityFavorites, matchesFilter, useCrudFilters, } from '@donotdev/crud';
21
+ import { useCrudCardList, EntityFilters, useEntityFavorites, useCrudFilters, } from '@donotdev/crud';
22
22
  import { CrudCard } from './CrudCardLink';
23
23
  /**
24
24
  * Entity Card List Component - Card grid view for public/user-facing browsing
@@ -35,33 +35,19 @@ export function EntityCardList({ entity, basePath, onClick, cols = [1, 2, 3, 4],
35
35
  filter, hideFilters = false, resultLabel, tone, }) {
36
36
  const navigate = useNavigate();
37
37
  const base = basePath ?? `/${entity.collection}`;
38
- // useCrudCardList -> handles fetching optimized for cards automatically
39
- const { data: listData, loading } = useCrudCardList(entity, { staleTime });
40
- const rawData = listData?.items || [];
38
+ // useCrudCardList -> handles fetching + client-side filter/search/sort via `processed`
39
+ const { items: rawData, processed, loading, } = useCrudCardList(entity, { staleTime });
41
40
  // Favorites - always enabled, no props needed
42
41
  const { isFavorite, toggleFavorite, favoritesFilter } = useEntityFavorites({
43
42
  collection: entity.collection,
44
43
  });
45
44
  // Favorites toggle from CrudStore (persists across navigation)
46
- // Note: EntityFilters now manages its own filters internally via useCrudFilters
47
- const { showFavoritesOnly, setShowFavoritesOnly, filters } = useCrudFilters({
45
+ const { showFavoritesOnly, setShowFavoritesOnly } = useCrudFilters({
48
46
  collection: entity.collection,
49
47
  });
50
- // Apply filters from EntityFilters component
51
- const applyFilters = useCallback((item) => {
52
- if (Object.keys(filters).length === 0)
53
- return true;
54
- return Object.entries(filters).every(([fieldName, filterValue]) => {
55
- const itemValue = item[fieldName];
56
- const fieldConfig = entity.fields[fieldName];
57
- const fieldType = fieldConfig?.type || 'text';
58
- return matchesFilter(itemValue, filterValue, fieldType);
59
- });
60
- }, [filters, entity.fields]);
61
- // Apply client-side filtering (including favorites)
48
+ // Apply UI-specific filters (favorites, consumer filter prop) on top of processed
62
49
  const data = useMemo(() => {
63
- let result = rawData;
64
- result = result.filter(applyFilters);
50
+ let result = processed;
65
51
  // Apply favorites filter if enabled
66
52
  if (showFavoritesOnly) {
67
53
  result = result.filter(favoritesFilter);
@@ -70,7 +56,7 @@ filter, hideFilters = false, resultLabel, tone, }) {
70
56
  result = result.filter(filter);
71
57
  }
72
58
  return result;
73
- }, [rawData, applyFilters, showFavoritesOnly, favoritesFilter, filter]);
59
+ }, [processed, showFavoritesOnly, favoritesFilter, filter]);
74
60
  // Entity + crud namespaces so formatValue can resolve crud:price.* etc.
75
61
  const { t } = useTranslation([entity.namespace, 'crud']);
76
62
  const { t: tCrud } = useTranslation('crud');
@@ -1 +1 @@
1
- {"version":3,"file":"EntityList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityList.tsx"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAmB,EACnB,UAAqB,EACrB,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,UAAiB,GAClB,EAAE,eAAe,2CAiTjB"}
1
+ {"version":3,"file":"EntityList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityList.tsx"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAmB,EACnB,UAAqB,EACrB,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,UAAiB,GAClB,EAAE,eAAe,2CA2QjB"}
@@ -9,7 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
9
9
  * **Routing:** Convention basePath = `/${collection}`. View/Edit = basePath/:id, Create = basePath/new.
10
10
  * Override basePath for nested routes; use onClick(id) to open sheet instead of navigating.
11
11
  *
12
- * @version 0.3.2
12
+ * @version 0.4.0
13
13
  * @since 0.0.1
14
14
  * @author AMBROISE PARK Consulting
15
15
  */
@@ -18,7 +18,7 @@ import { useMemo, useCallback, useState } from 'react';
18
18
  import { DataTable, Button, Stack, ActionButton, Section, Input, } from '@donotdev/components';
19
19
  import { useTranslation } from '@donotdev/core';
20
20
  import { useNavigate } from '../../routing';
21
- import { translateFieldLabel, useCrud, useCrudList, EntityFilters, matchesFilter, formatValue, useCrudFilters, } from '@donotdev/crud';
21
+ import { translateFieldLabel, useCrud, useCrudList, EntityFilters, formatValue, } from '@donotdev/crud';
22
22
  /**
23
23
  * Entity List Component - Table view for admin/internal operations
24
24
  *
@@ -37,11 +37,11 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
37
37
  // For server-side, we need to track pageSize state (DataTable will call onPageSizeChange)
38
38
  // For client-side, we just pass pageSizeProp to DataTable and it handles its own state
39
39
  const [serverPageSize, setServerPageSize] = useState(pageSizeProp);
40
- // Separation of Concerns:
41
- // Client mode: fetches ALL items, DataTable handles pagination
42
- // Server mode: fetches per page, controlled pagination
43
- const { data: listData, loading, mutate: refreshList, } = useCrudList(entity, {
40
+ const [searchQuery, setSearchQuery] = useState('');
41
+ // useCrudList now handles client-side filter + search + sort via `processed`
42
+ const { data: listData, items: rawData, processed: filteredData, loading, mutate: refreshList, } = useCrudList(entity, {
44
43
  pagination,
44
+ searchQuery,
45
45
  ...(queryOptions && { queryOptions }),
46
46
  ...(pagination === 'server' && {
47
47
  page: currentPage,
@@ -51,14 +51,8 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
51
51
  // useCrud -> handles actions (delete)
52
52
  const { delete: deleteItem } = useCrud(entity);
53
53
  const { t: tCrud } = useTranslation('crud');
54
- const data = listData?.items || [];
55
54
  // Entity + crud namespaces so formatValue can resolve crud:price.* etc.
56
55
  const { t } = useTranslation([entity.namespace, 'crud']);
57
- // Get filters for applying to data (EntityFilters manages its own state)
58
- const { filters } = useCrudFilters({
59
- collection: entity.collection,
60
- });
61
- const [searchQuery, setSearchQuery] = useState('');
62
56
  // Refresh handler - triggers manual refetch in useList
63
57
  const handleRefresh = useCallback(async () => {
64
58
  await refreshList();
@@ -84,36 +78,6 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
84
78
  const handleDelete = useCallback(async (itemId) => {
85
79
  await deleteItem(itemId);
86
80
  }, [deleteItem]);
87
- // Apply search and filters to data
88
- // @todo Server-side filtering: Currently only handles client-side filtering.
89
- // When pagination='server', filters should be sent to the server via useCrudList options.
90
- // Until then, filters are applied client-side after fetching.
91
- const filteredData = useMemo(() => {
92
- let result = data;
93
- // Apply search query (searches all fields)
94
- if (searchQuery) {
95
- const searchLower = searchQuery.toLowerCase();
96
- result = result.filter((item) => {
97
- return Object.values(item).some((value) => {
98
- if (value === null || value === undefined)
99
- return false;
100
- return String(value).toLowerCase().includes(searchLower);
101
- });
102
- });
103
- }
104
- // Apply column filters
105
- if (Object.keys(filters).length > 0) {
106
- result = result.filter((item) => {
107
- return Object.entries(filters).every(([fieldName, filterValue]) => {
108
- const itemValue = item[fieldName];
109
- const fieldConfig = entity.fields[fieldName];
110
- const fieldType = fieldConfig?.type || 'text';
111
- return matchesFilter(itemValue, filterValue, fieldType);
112
- });
113
- });
114
- }
115
- return result;
116
- }, [data, searchQuery, filters, entity.fields]);
117
81
  // Generate columns from entity.listFields or entity.fields
118
82
  const columns = useMemo(() => {
119
83
  const fieldsToShow = entity.listFields || Object.keys(entity.fields);
@@ -170,7 +134,7 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
170
134
  defaultValue: `Browse ${entityName} - Filters`,
171
135
  }), collapsible: true, defaultOpen: true, children: _jsxs(Stack, { children: [_jsxs(Stack, { direction: "row", gap: "tight", align: "center", className: "dndev-w-full", style: { display: 'grid', gridTemplateColumns: '1fr auto auto' }, children: [_jsx(Input, { placeholder: tCrud('search.placeholder', {
172
136
  defaultValue: 'Search...',
173
- }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), icon: Search, className: "dndev-w-full" }), _jsx(Button, { icon: RefreshCw, variant: "outline", onClick: handleRefresh, disabled: loading, display: "compact", "aria-label": tCrud('refresh', { defaultValue: 'Refresh' }) }), _jsx(Button, { icon: Plus, onClick: handleCreate, display: "compact", children: tCrud('addNew', { defaultValue: 'Add New' }) })] }), !hideFilters && (_jsx(EntityFilters, { entity: entity, data: data, fieldsToFilter: entity.listFields }))] }) }), _jsx(Section, { title: loading
137
+ }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), icon: Search, className: "dndev-w-full" }), _jsx(Button, { icon: RefreshCw, variant: "outline", onClick: handleRefresh, disabled: loading, display: "compact", "aria-label": tCrud('refresh', { defaultValue: 'Refresh' }) }), _jsx(Button, { icon: Plus, onClick: handleCreate, display: "compact", children: tCrud('addNew', { defaultValue: 'Add New' }) })] }), !hideFilters && (_jsx(EntityFilters, { entity: entity, data: rawData, fieldsToFilter: entity.listFields }))] }) }), _jsx(Section, { title: loading
174
138
  ? tCrud('results.title.fetching', {
175
139
  defaultValue: 'Fetching...',
176
140
  })
package/dist/dndev.css CHANGED
@@ -3422,80 +3422,120 @@ em {
3422
3422
 
3423
3423
  /* packages/components/src/atomic/Combobox/Combobox.css */
3424
3424
 
3425
- .dndev-combobox-trigger {
3426
- display: flex;
3427
- justify-content: space-between;
3428
- align-items: center;
3429
- width: 100%;
3430
- text-align: start;
3431
- }
3425
+ /* ──────────────────────────────────────────────────
3426
+ Trailing icon group — flex row inside the input area
3427
+ ────────────────────────────────────────────────── */
3432
3428
 
3433
- .dndev-combobox-trigger button {
3429
+ .dndev-combobox-trailing {
3430
+ position: absolute;
3431
+ inset-inline-end: var(--gap-sm);
3432
+ top: 0;
3433
+ height: 100%;
3434
3434
  display: flex;
3435
- justify-content: space-between;
3436
3435
  align-items: center;
3437
- width: 100%;
3436
+ gap: var(--gap-tight, 4px);
3437
+ z-index: 1;
3438
+ pointer-events: none; /* pass clicks through to input by default */
3438
3439
  }
3439
3440
 
3440
- .dndev-combobox-placeholder {
3441
- opacity: var(--opacity-muted);
3442
- }
3441
+ /* Input padding: reserve space for trailing content.
3442
+ --combobox-pad-end is set as an inline CSS variable on .dndev-combobox-wrapper. */
3443
3443
 
3444
- .dndev-combobox-trigger-icons {
3445
- display: flex;
3446
- align-items: center;
3447
- gap: var(--gap-tight);
3448
- margin-inline-start: auto;
3444
+ .dndev-combobox-wrapper .dndev-input {
3445
+ padding-inline-end: var(--combobox-pad-end);
3449
3446
  }
3450
3447
 
3451
- .dndev-combobox-clear {
3448
+ /* Action buttons (create "+", clear "X") */
3449
+
3450
+ .dndev-combobox-action-btn {
3451
+ all: unset;
3452
3452
  display: flex;
3453
3453
  align-items: center;
3454
3454
  justify-content: center;
3455
+ width: var(--icon-md);
3456
+ height: var(--icon-md);
3457
+ border-radius: var(--radius-interactive, var(--radius-sm, 4px));
3455
3458
  cursor: pointer;
3456
- opacity: var(--opacity-muted);
3457
- transition: opacity var(--dur-fast) var(--ease-in-out);
3458
- -webkit-user-select: none;
3459
- -moz-user-select: none;
3460
- user-select: none;
3459
+ pointer-events: auto; /* re-enable clicks on buttons */
3460
+ color: var(--muted-foreground);
3461
+ opacity: var(--opacity-muted, 0.5);
3462
+ transition:
3463
+ opacity var(--dur-fast) var(--ease-in-out),
3464
+ background-color var(--dur-fast) var(--ease-in-out);
3461
3465
  }
3462
3466
 
3463
- .dndev-combobox-clear:hover,
3464
- .dndev-combobox-clear:focus {
3467
+ .dndev-combobox-action-btn:hover {
3468
+ opacity: 1;
3469
+ background-color: var(--accent, rgba(0, 0, 0, 0.06));
3470
+ }
3471
+
3472
+ .dndev-combobox-action-btn:focus-visible {
3465
3473
  opacity: 1;
3466
3474
  outline: none;
3475
+ box-shadow:
3476
+ 0 0 0 2px var(--ring),
3477
+ 0 0 0 4px rgb(from var(--ring) r g b / 0.2);
3467
3478
  }
3468
3479
 
3469
- .dndev-combobox-clear svg {
3470
- width: var(--icon-sm);
3471
- height: var(--icon-sm);
3480
+ .dndev-combobox-action-btn:active {
3481
+ opacity: 0.8;
3482
+ }
3483
+
3484
+ .dndev-combobox-action-btn svg {
3485
+ width: var(--size-icon-sm, 16px);
3486
+ height: var(--size-icon-sm, 16px);
3487
+ }
3488
+
3489
+ /* Create button accent color */
3490
+
3491
+ .dndev-combobox-action-btn--create {
3492
+ color: var(--primary);
3493
+ }
3494
+
3495
+ /* Clear button */
3496
+
3497
+ .dndev-combobox-action-btn--clear {
3498
+ color: var(--muted-foreground);
3472
3499
  }
3473
3500
 
3501
+ /* Chevron indicator (non-interactive, just visual) */
3502
+
3474
3503
  .dndev-combobox-chevron {
3475
3504
  width: var(--icon-md);
3476
3505
  height: var(--icon-md);
3506
+ display: flex;
3507
+ align-items: center;
3508
+ justify-content: center;
3509
+ color: var(--muted-foreground);
3477
3510
  opacity: var(--opacity-muted);
3511
+ pointer-events: none;
3512
+ }
3513
+
3514
+ .dndev-combobox-chevron svg {
3515
+ width: var(--icon-md);
3516
+ height: var(--icon-md);
3478
3517
  transition: transform var(--dur-fast) var(--ease-in-out);
3479
3518
  }
3480
3519
 
3481
- [data-state='open'] .dndev-combobox-chevron {
3520
+ .dndev-combobox-open .dndev-combobox-chevron svg {
3482
3521
  transform: rotate(180deg);
3483
3522
  }
3484
3523
 
3485
- .dndev-combobox-loading-container {
3524
+ /* Spinner replaces chevron */
3525
+
3526
+ .dndev-combobox-spinner {
3486
3527
  display: flex;
3487
3528
  align-items: center;
3488
- gap: var(--gap-sm);
3489
- }
3490
-
3491
- .dndev-combobox-loading-spinner {
3529
+ justify-content: center;
3492
3530
  width: var(--icon-md);
3493
3531
  height: var(--icon-md);
3494
- border-radius: var(--radius-full);
3495
- border: 2px solid currentColor;
3496
- border-top-color: transparent;
3532
+ pointer-events: none;
3497
3533
  }
3498
3534
 
3535
+ /* ──────────────────────────────────────────────────
3536
+ Dropdown content
3537
+ ────────────────────────────────────────────────── */
3538
+
3499
3539
  .dndev-combobox-content {
3500
3540
  width: var(--radix-popover-trigger-width);
3501
3541
  min-width: var(--radix-popover-trigger-width);
@@ -3505,15 +3545,6 @@ em {
3505
3545
  padding: 0;
3506
3546
  }
3507
3547
 
3508
- .dndev-combobox-search-container {
3509
- padding: var(--gap-sm);
3510
- border-bottom: var(--border-width) solid var(--line-2);
3511
- }
3512
-
3513
- .dndev-combobox-search-input {
3514
- width: 100%;
3515
- }
3516
-
3517
3548
  .dndev-combobox-option {
3518
3549
  all: unset;
3519
3550
  display: flex;
@@ -3584,20 +3615,6 @@ em {
3584
3615
  flex-shrink: 0;
3585
3616
  }
3586
3617
 
3587
- .dndev-combobox-create-btn:hover {
3588
- opacity: 0.8;
3589
- }
3590
-
3591
- .dndev-combobox-open .dndev-input-with-trailing-icon .dndev-input-icon svg,
3592
- .dndev-combobox-open .dndev-input-with-trailing-icon .dndev-input-icon > * {
3593
- transform: rotate(180deg);
3594
- }
3595
-
3596
- .dndev-input-with-trailing-icon .dndev-input-icon svg,
3597
- .dndev-input-with-trailing-icon .dndev-input-icon > * {
3598
- transition: transform var(--dur-fast) var(--ease-in-out);
3599
- }
3600
-
3601
3618
  /* packages/components/src/atomic/DualCard/DualCard.css */
3602
3619
 
3603
3620
  .dndev-dual-card {
@@ -9498,7 +9515,10 @@ main[role='main'][data-routing-animation='none'] {
9498
9515
  max-height: none;
9499
9516
  overflow-y: visible;
9500
9517
  overflow-x: hidden;
9501
- grid-template-rows: var(--header-height) minmax(calc(100dvh - var(--header-height)), auto) auto;
9518
+ grid-template-rows: var(--header-height) minmax(
9519
+ calc(100dvh - var(--header-height)),
9520
+ auto
9521
+ ) auto;
9502
9522
  }
9503
9523
 
9504
9524
  .dndev-layout[data-footer-mode='scroll'] header[role='banner'] {
@@ -9539,7 +9559,10 @@ main[role='main'][data-routing-animation='none'] {
9539
9559
  max-height: none;
9540
9560
  overflow-y: visible;
9541
9561
  overflow-x: hidden;
9542
- grid-template-rows: var(--header-height) minmax(calc(100dvh - var(--header-height)), auto) auto;
9562
+ grid-template-rows: var(--header-height) minmax(
9563
+ calc(100dvh - var(--header-height)),
9564
+ auto
9565
+ ) auto;
9543
9566
  }
9544
9567
 
9545
9568
  .dndev-layout header[role='banner'] {