@gravitee/graphene-core 3.25.0 → 3.26.0-feat-educational-landing.e5cd2b7

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 (33) hide show
  1. package/USAGE_GUIDE.md +188 -34
  2. package/dist/composed/DataTable/DataTable.d.ts +14 -1
  3. package/dist/composed/DataTable/DataTable.d.ts.map +1 -1
  4. package/dist/composed/EducationalLanding/EducationalLanding.d.ts +62 -0
  5. package/dist/composed/EducationalLanding/EducationalLanding.d.ts.map +1 -0
  6. package/dist/composed/EducationalLanding/EducationalLandingComparison.d.ts +29 -0
  7. package/dist/composed/EducationalLanding/EducationalLandingComparison.d.ts.map +1 -0
  8. package/dist/composed/EducationalLanding/EducationalLandingFlow.d.ts +42 -0
  9. package/dist/composed/EducationalLanding/EducationalLandingFlow.d.ts.map +1 -0
  10. package/dist/composed/EducationalLanding/EducationalLandingPanel.d.ts +37 -0
  11. package/dist/composed/EducationalLanding/EducationalLandingPanel.d.ts.map +1 -0
  12. package/dist/composed/EducationalLanding/EducationalLandingPillar.d.ts +26 -0
  13. package/dist/composed/EducationalLanding/EducationalLandingPillar.d.ts.map +1 -0
  14. package/dist/composed/EducationalLanding/EducationalLandingPillars.d.ts +31 -0
  15. package/dist/composed/EducationalLanding/EducationalLandingPillars.d.ts.map +1 -0
  16. package/dist/composed/EducationalLanding/index.d.ts +13 -0
  17. package/dist/composed/EducationalLanding/index.d.ts.map +1 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +8232 -8010
  21. package/dist/styles/globals.css +1 -1
  22. package/package.json +4 -1
  23. package/snippets/educational-landing/EntitiesLanding.tsx +89 -0
  24. package/snippets/educational-landing/EntitiesLandingPage.tsx +108 -0
  25. package/snippets/educational-landing/snippet.json +5 -0
  26. package/snippets/list-page/DeleteEntityDialog.tsx +71 -0
  27. package/snippets/list-page/EntitiesPage.tsx +221 -0
  28. package/snippets/list-page/EntityActionsMenu.tsx +69 -0
  29. package/snippets/list-page/entities.service.ts +108 -0
  30. package/snippets/list-page/entityColumns.tsx +79 -0
  31. package/snippets/list-page/snippet.json +6 -0
  32. package/snippets/list-page/useEntityList.ts +142 -0
  33. package/snippets/data-table-list-page.tsx +0 -316
package/USAGE_GUIDE.md CHANGED
@@ -482,7 +482,7 @@ Follow these steps to apply the layout width system to an existing consumer modu
482
482
  4. **Remove ad-hoc width constraints** — delete any `max-w-*` classes on page-level wrapper divs. The design system owns content width.
483
483
  5. **Verify** — resize the browser to confirm content stays centered without clipping on narrow viewports.
484
484
 
485
- See `node_modules/@gravitee/graphene-core/snippets/data-table-list-page.tsx` and `creation-wizard-page.tsx` for complete examples.
485
+ See the folder `node_modules/@gravitee/graphene-core/snippets/list-page/` and `creation-wizard-page.tsx` for complete examples.
486
486
 
487
487
  ### Page header
488
488
 
@@ -602,11 +602,137 @@ export function ApisPage() {
602
602
  - Leave the placeholder as the only name: it disappears as soon as the user types. `aria-label` says what is searched.
603
603
  - Give the field a fixed width in a shared wrapper: the page sets it with `className` where the field lives, `w-64` in a toolbar, nothing in a full-width column.
604
604
 
605
+ ### Educational landing
606
+
607
+ A flagship page opens, on first use, with `EducationalLanding`: a card that asks why the entity exists ("Why create an LLM proxy?"), answers in two sentences, compares life without and with it in two tinted panels, and ends with its key capabilities. Use it when the entity is a product concept worth explaining (proxies, clusters, applications, alerts). The page renders it **instead of** the table once the list query has settled on an empty list, never while it loads, and keeps its `PageHeader` with the Add button above it. The same card can also show **on demand** above a populated list, with a dismiss button.
608
+
609
+ Do not use it for the empty state of a secondary list (`DataTableEmptyState variant="first-use"`), for a search or filter with no results (`variant="no-results"`), or on the home page.
610
+
611
+ The page composes the card from six parts and chooses their order; no part takes a style variant.
612
+
613
+ | Part | Props | Renders |
614
+ | --- | --- | --- |
615
+ | `EducationalLanding` | `heading`, `description`, `onDismiss?`, `className?`, `children` | The card, as a `<section>` named by its `<h2>` heading; the answer; when `onDismiss` is set, a dismiss button that hides the card and calls it; the children stacked with a rule between two |
616
+ | `EducationalLandingComparison` | `children`, `className?` | Two `Panel` side by side once the card's content is 44rem wide, the arrow between them; stacked below |
617
+ | `EducationalLandingPanel` | `tone` (`'before'` \| `'after'`), `label`, `points`, `children?`, `className?` | A tinted box: the label (`<h3>`), the free content (a `Flow`), the ✗ or ✓ points |
618
+ | `EducationalLandingFlow` | `steps` (`{ icon, label, emphasized?, multi? }[]`), `accent?` (`'primary'` \| `'highlight'`), `className?` | An ordered list of icon steps joined by arrows, stacked vertically when one row does not fit; `emphasized` boxes the entity, `multi` stacks three chips for a 1 to n step |
619
+ | `EducationalLandingPillars` | `columns?` (2, 3 or 4, default 3), `children`, `className?` | A grid list, one column in a narrow card |
620
+ | `EducationalLandingPillar` | `icon`, `title`, `description`, `className?` | An icon chip, a title (`<h3>`), one sentence |
621
+
622
+ A `Panel` or a `Flow` alone is a valid part, and two `Comparison` in a row simply stack. The parts lay out on the card's width, not the viewport's: the card sits next to the sidebar. The canonical composition, the one the snippet reproduces:
623
+
624
+ ```tsx
625
+ import {
626
+ Button,
627
+ EducationalLanding,
628
+ EducationalLandingComparison,
629
+ EducationalLandingFlow,
630
+ EducationalLandingPanel,
631
+ EducationalLandingPillar,
632
+ EducationalLandingPillars,
633
+ PageHeader,
634
+ } from '@gravitee/graphene-core';
635
+ import {
636
+ BarChart3Icon,
637
+ GlobeIcon,
638
+ MonitorIcon,
639
+ PlusIcon,
640
+ ServerIcon,
641
+ ShieldIcon,
642
+ WaypointsIcon,
643
+ } from '@gravitee/graphene-core/icons';
644
+ import { Link } from 'react-router-dom';
645
+
646
+ export function LlmProxiesLandingPage() {
647
+ // Your module's permission check. Gamma modules use `useHasPermission` from `@gravitee/gamma-modules-sdk`.
648
+ const canCreate = true;
649
+
650
+ return (
651
+ <div className="flex flex-col gap-6">
652
+ <PageHeader
653
+ title="LLM Proxies"
654
+ description="Smart routing and governance for AI model requests"
655
+ actions={
656
+ canCreate && (
657
+ <Button asChild>
658
+ <Link to="new">
659
+ <PlusIcon />
660
+ Create LLM proxy
661
+ </Link>
662
+ </Button>
663
+ )
664
+ }
665
+ />
666
+ <EducationalLanding
667
+ heading="Why create an LLM proxy?"
668
+ description="An LLM proxy puts the AI Gateway between your applications and the model providers, so every model request goes through one place you control."
669
+ >
670
+ <EducationalLandingComparison>
671
+ <EducationalLandingPanel
672
+ tone="before"
673
+ label="Direct model calls"
674
+ points={['Each app wired to its own provider', 'Provider API keys spread across every app', 'No record of who sent which request']}
675
+ >
676
+ <EducationalLandingFlow
677
+ steps={[
678
+ { icon: MonitorIcon, label: 'App' },
679
+ { icon: ServerIcon, label: 'Model provider' },
680
+ ]}
681
+ />
682
+ </EducationalLandingPanel>
683
+ <EducationalLandingPanel
684
+ tone="after"
685
+ label="With an LLM proxy"
686
+ points={['One endpoint for every model provider', 'Provider API keys kept in the gateway', 'Every request logged with its tokens and cost']}
687
+ >
688
+ <EducationalLandingFlow
689
+ steps={[
690
+ { icon: MonitorIcon, label: 'App' },
691
+ { icon: GlobeIcon, label: 'LLM proxy', emphasized: true },
692
+ { icon: ServerIcon, label: 'Model providers' },
693
+ ]}
694
+ />
695
+ </EducationalLandingPanel>
696
+ </EducationalLandingComparison>
697
+ <EducationalLandingPillars>
698
+ <EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Pick the model for each request by cost, latency, load or semantic fit." />
699
+ <EducationalLandingPillar icon={BarChart3Icon} title="Cost control" description="Budgets, rate limits and spend alerts keep each application's LLM costs predictable." />
700
+ <EducationalLandingPillar icon={ShieldIcon} title="Resilience" description="Health-aware backends take a failing provider out of rotation until it recovers." />
701
+ </EducationalLandingPillars>
702
+ </EducationalLanding>
703
+ </div>
704
+ );
705
+ }
706
+ ```
707
+
708
+ The page decides between the landing and the table once the list query has settled, never from the previous page kept while the next one loads (`isPlaceholderData`): `settled && totalCount === 0 && !hasCriteria` renders the landing, anything else renders the table with its own loading, error and no-results states. `useEntityList` in the list page snippet returns that `settled` flag. Deciding on `isSuccess` alone shows the landing for a moment when a search with no results is cleared.
709
+
710
+ **On demand.** Above a populated list, pass `onDismiss`: the card shows a dismiss button, and a click hides the card, then calls `onDismiss`. A reload, or the page rendering the card again, shows it again. To reopen it without a reload, the page keeps a `showInfo` state and a toggle in `PageHeader` `actions` (an icon button with an `aria-label` and `aria-pressed={showInfo}`), renders the landing when `showInfo` is true, and passes `onDismiss={() => { setShowInfo(false); toggleRef.current?.focus(); }}` so focus goes back to the toggle. Remembering the dismissal (localStorage) stays in the module.
711
+
712
+ Copy-paste starting point: `node_modules/@gravitee/graphene-core/snippets/educational-landing/` (`npx graphene-sync-ai-rules` copies it to `.claude/graphene-snippets/` and `.cursor/rules/graphene-snippets/`), the page and the card in two files, with the on-demand variant as a commented addendum. Storybook: **Patterns/Educational Landing** renders the snippet's page, **Composed/EducationalLanding** shows the card and the layouts its parts allow.
713
+
714
+ **Do not:**
715
+
716
+ - Put a button or a link in the card besides the dismiss button: the Add button stays in `PageHeader`, in every state.
717
+ - Use it as an empty state: not for a secondary list, not for a search with no results.
718
+ - Render more than one per page, or change its heading: it is always an `<h2>` under the page's `<h1>`.
719
+ - Tint or restyle it through `className`: no custom color, no shadow. `className` is for the page's layout.
720
+ - Build a flow by hand, icons and arrows in a `div`: pass `steps` to `EducationalLandingFlow`.
721
+ - Repeat a ✓ point as a pillar: the points say what changes for the user, the pillars name the capabilities behind it.
722
+
723
+ **Migrating a landing from the field:**
724
+
725
+ | Today | With the parts |
726
+ | --- | --- |
727
+ | aim `EducationalEmptyState` | `title`, `tagline`, `cta` → `PageHeader` `title`, `description`, `actions`; `why.question`, `why.answer` → `heading`, `description`; `without`, `withCatalog` → `Panel tone="before"`, `Panel tone="after"` in a `Comparison` (`bullets` → `points`, `flow` → `Flow` steps); `pillars` → `Pillars`; `FlowNode` `emphasized` → the step's `emphasized`, with `accent="highlight"` to keep the violet |
728
+ | esm `EducationalLanding` (`LandingConfig`) | `heading`, `description` → the same props; `without`, `with` → two `Panel` in a `Comparison` (`flow` → `Flow` steps, `highlighted` → `emphasized`, `multi` unchanged); `features` → `Pillars`; `whatIs` → a block of the module's own, first child of the card; `onDismiss` unchanged, `useDismissed` stays in the module |
729
+ | apim and platform hand-written landings | The card → `EducationalLanding`, its "Why …?" line → `heading`; tinted boxes → `Panel`; icon rows → `Flow`; feature tiles → `Pillars`; an extra diagram or comparison stays a child block of the card |
730
+
605
731
  ### Data table (entity list pages)
606
732
 
607
733
  Use `DataTable` for any entity list — whether the data is fetched page-by-page from an API or loaded in full on the client. The component provides sorting, filtering, pagination, column visibility, row selection, and bulk actions through a composable slot API.
608
734
 
609
- **To implement:** copy the snippet from `node_modules/@gravitee/graphene-core/snippets/data-table-list-page.tsx` and replace the entity type, column definitions, and fetch function with your data. See Storybook **Patterns/Data Table → ApiList** for the full interactive example.
735
+ **To implement:** copy the folder `node_modules/@gravitee/graphene-core/snippets/list-page/` into your module, rename `Entity` and `entities` after your resource, and point `useEntityList.ts` at your API client instead of the sample `entities.service.ts`. Its `snippet.json` lists the packages it needs beyond Graphene's peers (`@tanstack/react-query`, `react-router-dom`). See Storybook **Patterns/List Page** for the page in its five states.
610
736
 
611
737
  #### When to use DataTable vs raw Table
612
738
 
@@ -656,11 +782,12 @@ Use `DataTable` for any entity list — whether the data is fetched page-by-page
656
782
 
657
783
  #### Search and filters
658
784
 
659
- - Search and filters live in the DataTable `toolbar` slot.
660
- - Search: `SearchField` in the toolbar with `className="w-64"`; it debounces 300 ms and reports the settled query, reset the page to 1 in `onValueChange`.
661
- - **Do NOT add a search field to a server-paginated table unless the backend endpoint supports a text search parameter** (`query`, `q`, or equivalent). Client-side search on a single page of server-paginated data is misleading — users will miss matches on other pages. If the backend does not support search, either request the backend change or omit the search field entirely.
662
- - Use `FacetedFilter` for multi-select categorical filters.
663
- - Show a "Reset" button when any filter is active.
785
+ - Search and filters live in the DataTable `toolbar` slot, inside `<div className="flex min-w-0 flex-wrap items-center gap-2">`: DataTable's own toolbar row does not wrap, this container does. Group the filters and Reset in a nested `flex flex-wrap items-center gap-2` so that they wrap together.
786
+ - Search: `SearchField` first, with an `aria-label` that says what is searched, a placeholder, and `className="w-64"`. It debounces 300 ms and reports the settled query: reset the page to 1 in `onValueChange`.
787
+ - **Do NOT add a search field to a server-paginated table unless the backend endpoint supports a text search parameter** (`query`, `q`, or equivalent). Client-side search on a single page of server-paginated data is misleading: users will miss matches on other pages. If the backend does not support search, either request the backend change or omit the search field entirely.
788
+ - Single-choice filter: a `Select` whose default option is "All ..." ("All statuses"), named by a `Label` in `sr-only`, with a minimum width (`min-w-32`) so that it does not jump as its value changes.
789
+ - Multiple-choice filter: a `FacetedFilter`.
790
+ - Reset: a `Button variant="ghost" size="sm"`, rendered only while a criterion is active. It clears every criterion and the page, then moves the focus to the search field (pass a `ref` to `SearchField`).
664
791
 
665
792
  #### Row navigation
666
793
 
@@ -675,6 +802,9 @@ Use **name-column-as-link**: the primary identifier column (always leftmost) is
675
802
  - **Quick-action escape hatch:** one high-discoverability action (e.g. "Deploy") may appear as an inline icon *alongside* the dropdown, but the dropdown must still exist.
676
803
  - Use `MoreVerticalIcon` (⋮) for table rows. Do not use `MoreHorizontalIcon` — the horizontal variant is for toolbars and card headers.
677
804
  - Destructive actions go last in the menu, after a `DropdownMenuSeparator`.
805
+ - Name the ⋮ button after its row: ``aria-label={`Actions for ${entity.name}`}``.
806
+ - A destructive action asks for confirmation in a `Dialog` before it runs, then reports the outcome in a toast (`toast` from `@gravitee/graphene-core`, shown by the app's `Toaster`). When the dialog closes, give the focus back to the ⋮ if the user cancelled. Once the entity is deleted, its row and ⋮ go: move the focus to the search field, as Reset does. See `EntityActionsMenu.tsx` and `DeleteEntityDialog.tsx` in the list page snippet.
807
+ - Key the rows by entity (`tableOptions={{ getRowId: (entity) => entity.id }}`), so that a removed row never hands its focused ⋮ to the next entity. Not with `selectionMode="multi"` for now: `onSelectionChange` still maps the selection by row index.
678
808
 
679
809
  #### Column ordering
680
810
 
@@ -688,7 +818,9 @@ Use **name-column-as-link**: the primary identifier column (always leftmost) is
688
818
 
689
819
  **Minimum 3 data columns** (excluding actions). If an entity has fewer than 3 meaningful attributes, use a simpler component (Item list, card grid) instead of DataTable.
690
820
 
691
- Target **4-7 visible columns**. More than 7 → enable column visibility and hide low-priority columns by default. Do NOT show raw entity IDs as visible columns.
821
+ Target **4-7 visible columns**. List pages show the View menu (`enableColumnVisibility`), with the name and the actions always visible (`enableHiding: false`); more than 7 → hide the low-priority columns by default. Do NOT show raw entity IDs as visible columns.
822
+
823
+ `enableColumnVisibility` adds the View menu, which names each column by its `meta.label`, else by its header when that is text: give a column whose header is a component, such as a sortable `DataTableColumnHeader`, a `meta: { label: 'Created' }`, or the menu shows its id.
692
824
 
693
825
  #### Cell renderers
694
826
 
@@ -704,56 +836,61 @@ Import from `@gravitee/graphene-core`:
704
836
 
705
837
  #### Empty states
706
838
 
707
- Use `DataTableEmptyState` — it enforces the correct structure for both scenarios:
839
+ A flagship list renders `EducationalLanding` instead of the first-use empty state: see [Educational landing](#educational-landing).
840
+
841
+ Use `DataTableEmptyState` for both cases, and decide only once the query has settled: never conclude "empty" from the previous page kept while the next one loads (`isPlaceholderData`).
708
842
 
709
- **`variant="first-use"`** — collection is genuinely empty (user has never created entities)
710
- - Render **instead of** `DataTable` (no table chrome needed)
711
- - Wrap in `<div className="rounded-lg border">` for containment
712
- - Props: `icon`, `title`, `description`, `primaryAction`, optional `secondaryAction`, optional `children` for educational content (flow diagrams, feature pillars)
713
- - Decision: `totalCount === 0 && !hasActiveFilters`
843
+ **`variant="first-use"`**: the collection is empty and no criterion is active.
844
+ - Render it **instead of** `DataTable`, wrapped in `<div className="rounded-lg border">`.
845
+ - In a list page: an icon, a title, one sentence, and **no button**. The header's Add button is the one creation CTA (see below).
846
+ - `primaryAction`, `secondaryAction` and `children` exist for other surfaces. The first use of a flagship page is not one of them: it renders `EducationalLanding`, a card without a button (see [Educational landing](#educational-landing)).
847
+ - Decision: `totalCount === 0 && !hasCriteria`.
714
848
 
715
- **`variant="no-results"`** — active filters/search returned zero matches
716
- - Render **inside** `DataTable` as the `emptyMessage` prop (keep toolbar visible)
717
- - Props: `icon` (use `SearchIcon`), `title`, `description`, `action` (e.g. "Clear filters")
718
- - Decision: `filteredCount === 0 && hasActiveFilters`
849
+ **`variant="no-results"`**: a search or a filter matched nothing.
850
+ - Render it **inside** `DataTable` as `emptyMessage`, so the toolbar stays.
851
+ - `icon={<SearchIcon />}`, a title ("No APIs found"), one sentence inviting to adjust the search or the filters, and **no button**: the search field's clear button and the toolbar's Reset clear the criteria.
852
+ - Its `action` slot exists for the rare table without a Reset in its toolbar.
853
+ - Decision: `totalCount === 0 && hasCriteria`.
719
854
 
720
855
  ```tsx
721
- // Page-level decision pattern
856
+ // Page-level decision, once the query has settled
722
857
  {isFirstUse ? (
723
858
  <div className="rounded-lg border">
724
- <DataTableEmptyState variant="first-use" icon={<GlobeIcon />} title="No APIs yet" ... />
859
+ <DataTableEmptyState variant="first-use" icon={<GlobeIcon />} title="No APIs yet" description="..." />
725
860
  </div>
726
861
  ) : (
727
862
  <DataTable
728
- emptyMessage={<DataTableEmptyState variant="no-results" icon={<SearchIcon />} ... />}
863
+ emptyMessage={<DataTableEmptyState variant="no-results" icon={<SearchIcon />} title="No APIs found" description="..." />}
729
864
  ...
730
865
  />
731
866
  )}
732
867
  ```
733
868
 
734
- See Storybook **Composed/DataTableEmptyState → Integration** for the full interactive example.
869
+ See Storybook **Patterns/List Page** (states `empty` and `no-results`) for the full interactive example.
735
870
 
736
871
  #### Header button coordination
737
872
 
738
- Pages with a primary "Add Entity" button in the header follow one rule: **the header button only appears when data exists**. During first-use, the empty state owns the primary CTA.
873
+ The Add button stays in the header in every state: one creation CTA on screen.
739
874
 
740
- | State | Header button | Content area |
875
+ | State | Header | Content area |
741
876
  |---|---|---|
742
- | **First-use** (`totalCount === 0 && !hasFilters`) | Hidden | `DataTableEmptyState variant="first-use"` with primary CTA |
743
- | **Has data** (`totalCount > 0`) | Primary `+ Add Entity` | DataTable |
744
- | **No results** (filters active, zero matches) | Primary `+ Add Entity` | `DataTableEmptyState variant="no-results"` with "Clear filters" |
877
+ | **First use** (`totalCount === 0`, no criterion) | Add | `DataTableEmptyState variant="first-use"` instead of the table, without a button |
878
+ | **Data** (`totalCount > 0`) | Add | `DataTable` |
879
+ | **No results** (a criterion active, zero matches) | Add | `DataTable` with `DataTableEmptyState variant="no-results"` inside, without a button |
745
880
 
746
- Both the header button and the empty state CTA trigger the same creation flow. No visual downgrade (both are primary variant buttons). No transition animation between states.
881
+ Gate the button on the permission, never on the list's state:
747
882
 
748
883
  ```tsx
749
884
  <PageHeader
750
- title="Entities"
751
- description="Manage your entity catalog."
885
+ title="APIs"
886
+ description="Design, deploy and secure the APIs your gateways expose to consumers."
752
887
  actions={
753
- !isFirstUse && (
754
- <Button size="sm">
755
- <PlusIcon />
756
- Add entity
888
+ canCreate && (
889
+ <Button asChild>
890
+ <Link to="new">
891
+ <PlusIcon />
892
+ Add API
893
+ </Link>
757
894
  </Button>
758
895
  )
759
896
  }
@@ -762,9 +899,26 @@ Both the header button and the empty state CTA trigger the same creation flow. N
762
899
 
763
900
  #### Loading state
764
901
 
902
+ - Fetch with TanStack Query and `placeholderData: keepPreviousData`, and pass `loading={isFetching}`: the first load shows skeleton rows, and later fetches keep the rows under DataTable's overlay. There is no code to write for either.
765
903
  - Pass `skeletonCount={pageSize}` to prevent layout shift.
766
904
  - `loadingDelay={200}` (default) prevents flash on fast responses.
767
905
 
906
+ #### Error state
907
+
908
+ - A destructive `Alert` above the table: a short title ("Could not load APIs") and a Retry button that calls `refetch()`, disabled while it runs.
909
+ - Keep the last loaded page on screen under the alert, with the toolbar and the pagination, and say that it may be out of date. `keepPreviousData` holds the previous page only while the next one loads, so keep the last successful response in your list hook: the search field must never disappear while the user types.
910
+ - When nothing has loaded yet, the alert alone, with the error message.
911
+
912
+ #### List state in the URL
913
+
914
+ Keep the list state in the URL with `useSearchParams` (react-router-dom), so that a reload, a shared link and the Back button restore it:
915
+
916
+ - Keys: `q` for the search, one key per filter (`status`), repeated for a multiple choice (`type=internal&type=partner`), `page`, `size`, and `sort` as `field:asc` or `field:desc`.
917
+ - Defaults are not written, and every value is read with a fallback: an unknown status, size or sort gives the default.
918
+ - A new criterion (search, filter, sort) deletes `page` and replaces the history entry; changing the page or its size adds one.
919
+ - Give the search back to `SearchField` exactly as it reported it, and trim it where it is used (the request).
920
+ - A page past the last one (its last row deleted, an old link) goes to the last page.
921
+
768
922
  #### `serverSide` prop
769
923
 
770
924
  Set `serverSide` when data is fetched page-by-page from an API. It disables TanStack's built-in sort/filter/pagination models so you control everything via your fetch logic.
@@ -1,5 +1,14 @@
1
- import { ColumnDef, OnChangeFn, SortingState, TableOptions } from '@tanstack/react-table';
1
+ import { ColumnDef, OnChangeFn, RowData, SortingState, TableOptions } from '@tanstack/react-table';
2
2
  import { ReactNode } from 'react';
3
+ declare module '@tanstack/react-table' {
4
+ interface ColumnMeta<TData extends RowData, TValue> {
5
+ /**
6
+ * The column's name in the View menu (`enableColumnVisibility`), for a header that is not text,
7
+ * such as a sortable `DataTableColumnHeader`. Without it, the menu shows the column id.
8
+ */
9
+ label?: string;
10
+ }
11
+ }
3
12
  interface DataTablePaginationConfig {
4
13
  readonly page: number;
5
14
  readonly pageSize: number;
@@ -21,6 +30,10 @@ interface DataTableProps<TData> {
21
30
  /** Called when the user clicks a sortable column header. Use to trigger a server re-fetch with the new sort params. */
22
31
  readonly onSortingChange?: OnChangeFn<SortingState>;
23
32
  readonly emptyMessage?: ReactNode;
33
+ /**
34
+ * Adds the View menu, which shows and hides the columns whose `enableHiding` is not false. It names a
35
+ * column by its `meta.label`, else by its header when that is text, else by its id.
36
+ */
24
37
  readonly enableColumnVisibility?: boolean;
25
38
  readonly enableColumnResizing?: boolean;
26
39
  /** Makes the table header sticky. Consumer should set a max-height via className. */
@@ -1 +1 @@
1
- {"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/composed/DataTable/DataTable.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/composed/DataTable/DataTable.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAMd,KAAK,UAAU,EAEf,KAAK,OAAO,EAEZ,KAAK,YAAY,EACjB,KAAK,YAAY,EAGlB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAqD,MAAM,OAAO,CAAC;AAQ1F,OAAO,QAAQ,uBAAuB,CAAC;IAGrC,UAAU,UAAU,CAAC,KAAK,SAAS,OAAO,EAAE,MAAM;QAChD;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB;CACF;AAeD,UAAU,yBAAyB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,iCAAiC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC;AAED,UAAU,cAAc,CAAC,KAAK;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACrD,wGAAwG;IACxG,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;IACjC,0HAA0H;IAC1H,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,uHAAuH;IACvH,QAAQ,CAAC,eAAe,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,qFAAqF;IACrF,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,yGAAyG;IACzG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iGAAiG;IACjG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IAChD,wHAAwH;IACxH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,+FAA+F;IAC/F,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;CACtD;AAqCD,iBAAS,SAAS,CAAC,KAAK,EAAE,EACxB,OAAO,EAAE,WAAW,EACpB,IAAI,EACJ,aAAsB,EACtB,iBAAiB,EACjB,WAAW,EACX,OAAO,EAAE,WAAW,EACpB,eAAe,EAAE,mBAAmB,EACpC,YAA4B,EAC5B,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,OAAO,EAAE,WAAW,EACpB,YAAkB,EAClB,aAAiB,EACjB,SAAS,EACT,YAAY,EAAE,SAAS,EACvB,OAAO,EACP,UAAU,EACV,MAAM,EACN,UAAU,EACV,YAAY,GACb,EAAE,cAAc,CAAC,KAAK,CAAC,+BAwOvB;AAED,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,YAAY,EAAE,yBAAyB,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { ReactNode } from 'react';
2
+ interface EducationalLandingProps {
3
+ /** Why the entity exists, as a question: "Why create an LLM proxy?". Rendered as an `<h2>` that names the section. */
4
+ readonly heading: string;
5
+ /** The answer, two sentences at most, in 14px muted text. */
6
+ readonly description: string;
7
+ /**
8
+ * Shows a dismiss button at the top right: the card shown on demand above a populated list. A click
9
+ * hides the card, then calls `onDismiss`, where the page remembers the choice and moves focus to
10
+ * the control that reopens the card. A reload, or the page rendering the card again, shows it again.
11
+ */
12
+ readonly onDismiss?: () => void;
13
+ /** Merged on the root `<section>`. */
14
+ readonly className?: string;
15
+ /**
16
+ * The parts, stacked in the order written with a rule between two: `EducationalLandingComparison`,
17
+ * `EducationalLandingPanel`, `EducationalLandingFlow`, `EducationalLandingPillars`.
18
+ */
19
+ readonly children: ReactNode;
20
+ }
21
+ /**
22
+ * The educational landing of a flagship page: a card that asks why the entity exists, answers in
23
+ * two sentences, then shows the parts the page composes (a comparison of two tinted panels, a flow,
24
+ * pillars). On first use the page renders it instead of the table, under its `PageHeader`; on
25
+ * demand, above the table with `onDismiss`, and its dismiss button hides it.
26
+ *
27
+ * The heading is always an `<h2>`: the page's `<h1>` and its Add button stay in `PageHeader`, and
28
+ * the card holds no button or link besides the dismiss button. It is not an empty state: the empty
29
+ * state of a secondary list is `DataTableEmptyState`.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * <div className="flex flex-col gap-6">
34
+ * <PageHeader title="LLM Proxies" actions={canCreate && <Button><PlusIcon />Create LLM proxy</Button>} />
35
+ * <EducationalLanding
36
+ * heading="Why create an LLM proxy?"
37
+ * description="An LLM proxy routes your applications' model requests through the AI Gateway."
38
+ * >
39
+ * <EducationalLandingComparison>
40
+ * <EducationalLandingPanel tone="before" label="Direct model calls" points={withoutPoints}>
41
+ * <EducationalLandingFlow steps={[{ icon: MonitorIcon, label: 'App' }, { icon: ServerIcon, label: 'Model provider' }]} />
42
+ * </EducationalLandingPanel>
43
+ * <EducationalLandingPanel tone="after" label="With an LLM proxy" points={withPoints}>
44
+ * <EducationalLandingFlow steps={withSteps} />
45
+ * </EducationalLandingPanel>
46
+ * </EducationalLandingComparison>
47
+ * <EducationalLandingPillars>
48
+ * <EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Steer each request to the right model." />
49
+ * <EducationalLandingPillar icon={BarChart3Icon} title="Cost control" description="Keep LLM spend predictable." />
50
+ * <EducationalLandingPillar icon={ShieldIcon} title="Resilience" description="Fail over when a provider is down." />
51
+ * </EducationalLandingPillars>
52
+ * </EducationalLanding>
53
+ * </div>
54
+ * ```
55
+ */
56
+ declare function EducationalLanding({ heading, description, onDismiss, className, children }: EducationalLandingProps): import("react").JSX.Element | null;
57
+ declare namespace EducationalLanding {
58
+ var displayName: string;
59
+ }
60
+ export { EducationalLanding };
61
+ export type { EducationalLandingProps };
62
+ //# sourceMappingURL=EducationalLanding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EducationalLanding.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLanding.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,OAAO,CAAC;AAKxD,UAAU,uBAAuB;IAC/B,sHAAsH;IACtH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,sCAAsC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,iBAAS,kBAAkB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,uBAAuB,sCAsC5G;kBAtCQ,kBAAkB;;;AA0C3B,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { ReactNode } from 'react';
2
+ /** True inside `EducationalLandingComparison`: the "after" panel then draws the arrow on its left. */
3
+ declare const ComparisonContext: import('react').Context<boolean>;
4
+ interface EducationalLandingComparisonProps {
5
+ /** Two `EducationalLandingPanel`: the "before" panel first, then the "after" one. */
6
+ readonly children: ReactNode;
7
+ readonly className?: string;
8
+ }
9
+ /**
10
+ * Life without and with the entity, side by side: two `EducationalLandingPanel` of equal width
11
+ * once the card's content is 44rem wide, with an arrow between them drawn by the "after" panel;
12
+ * stacked, without the arrow, in a narrower card. 44rem is where a three-step flow fits in each
13
+ * panel. The page writes the "before" panel first.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <EducationalLandingComparison>
18
+ * <EducationalLandingPanel tone="before" label="Direct model calls" points={withoutPoints} />
19
+ * <EducationalLandingPanel tone="after" label="With an LLM proxy" points={withPoints} />
20
+ * </EducationalLandingComparison>
21
+ * ```
22
+ */
23
+ declare function EducationalLandingComparison({ children, className }: EducationalLandingComparisonProps): import("react").JSX.Element;
24
+ declare namespace EducationalLandingComparison {
25
+ var displayName: string;
26
+ }
27
+ export { ComparisonContext, EducationalLandingComparison };
28
+ export type { EducationalLandingComparisonProps };
29
+ //# sourceMappingURL=EducationalLandingComparison.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EducationalLandingComparison.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingComparison.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,sGAAsG;AACtG,QAAA,MAAM,iBAAiB,kCAAuB,CAAC;AAE/C,UAAU,iCAAiC;IACzC,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAS,4BAA4B,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,iCAAiC,+BAc/F;kBAdQ,4BAA4B;;;AAkBrC,OAAO,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,CAAC;AAC3D,YAAY,EAAE,iCAAiC,EAAE,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { LucideIcon } from '../../icons';
2
+ interface EducationalLandingFlowStep {
3
+ /** A Lucide icon from `@gravitee/graphene-core/icons`, drawn in a chip over the label. */
4
+ readonly icon: LucideIcon;
5
+ /** One or two words: "App", "LLM proxy", "Model providers". */
6
+ readonly label: string;
7
+ /** Boxes the step and tints it with the accent: the entity the landing is about. One per flow. */
8
+ readonly emphasized?: boolean;
9
+ /** Three chips, one solid and two dashed: a step that stands for many of one kind (1 to n clusters). */
10
+ readonly multi?: boolean;
11
+ }
12
+ interface EducationalLandingFlowProps {
13
+ /** The steps, in order; an arrow joins two consecutive steps. */
14
+ readonly steps: readonly EducationalLandingFlowStep[];
15
+ /** The tint of the emphasized step: `primary` by default, `highlight` (violet) where a product uses it. */
16
+ readonly accent?: 'primary' | 'highlight';
17
+ readonly className?: string;
18
+ }
19
+ /**
20
+ * A row of icon steps joined by arrows: how a request or an event travels, without and with the
21
+ * entity. Rendered as an ordered list, centered; when the steps lack room for one row, they stack,
22
+ * arrows pointing down. Usually the content of an `EducationalLandingPanel`; also valid alone, as a
23
+ * direct child of `EducationalLanding`.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <EducationalLandingFlow
28
+ * steps={[
29
+ * { icon: MonitorIcon, label: 'App' },
30
+ * { icon: GlobeIcon, label: 'LLM proxy', emphasized: true },
31
+ * { icon: ServerIcon, label: 'Model providers' },
32
+ * ]}
33
+ * />
34
+ * ```
35
+ */
36
+ declare function EducationalLandingFlow({ steps, accent, className }: EducationalLandingFlowProps): import("react").JSX.Element;
37
+ declare namespace EducationalLandingFlow {
38
+ var displayName: string;
39
+ }
40
+ export { EducationalLandingFlow };
41
+ export type { EducationalLandingFlowProps, EducationalLandingFlowStep };
42
+ //# sourceMappingURL=EducationalLandingFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EducationalLandingFlow.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingFlow.tsx"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAShF,UAAU,0BAA0B;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,wGAAwG;IACxG,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,2BAA2B;IACnC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACtD,2GAA2G;IAC3G,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAwCD;;;;;;;;;;;;;;;;GAgBG;AACH,iBAAS,sBAAsB,CAAC,EAAE,KAAK,EAAE,MAAkB,EAAE,SAAS,EAAE,EAAE,2BAA2B,+BAuDpG;kBAvDQ,sBAAsB;;;AA2D/B,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAClC,YAAY,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { ReactNode } from 'react';
2
+ interface EducationalLandingPanelProps {
3
+ /** `before`: life without the entity, tinted red with ✗ points. `after`: life with it, tinted with the primary color and ✓ points. */
4
+ readonly tone: 'before' | 'after';
5
+ /** A short noun phrase naming the situation: "Direct model calls", "With an LLM proxy". Rendered as an `<h3>`. */
6
+ readonly label: string;
7
+ /** Three short points, one line each, listed under the content. */
8
+ readonly points: readonly string[];
9
+ /** Free content between the label and the points: an `EducationalLandingFlow` in the canonical composition. */
10
+ readonly children?: ReactNode;
11
+ /** Merged on the tinted box. */
12
+ readonly className?: string;
13
+ }
14
+ /**
15
+ * A tinted box that describes one situation: its label, optional content (a flow), then a ✗ or ✓
16
+ * list. Two of them make an `EducationalLandingComparison`; one alone, as a direct child of
17
+ * `EducationalLanding`, describes a single situation. Inside a comparison laid out side by side,
18
+ * the `after` panel draws the arrow from the panel before it.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <EducationalLandingPanel
23
+ * tone="after"
24
+ * label="With an LLM proxy"
25
+ * points={['Routing across providers', 'Budgets and rate limits', 'Logs with token and cost']}
26
+ * >
27
+ * <EducationalLandingFlow steps={withSteps} />
28
+ * </EducationalLandingPanel>
29
+ * ```
30
+ */
31
+ declare function EducationalLandingPanel({ tone, label, points, children, className }: EducationalLandingPanelProps): import("react").JSX.Element;
32
+ declare namespace EducationalLandingPanel {
33
+ var displayName: string;
34
+ }
35
+ export { EducationalLandingPanel };
36
+ export type { EducationalLandingPanelProps };
37
+ //# sourceMappingURL=EducationalLandingPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EducationalLandingPanel.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAO,MAAM,OAAO,CAAC;AAuB5C,UAAU,4BAA4B;IACpC,sIAAsI;IACtI,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,kHAAkH;IAClH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,+GAA+G;IAC/G,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B,gCAAgC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,iBAAS,uBAAuB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,4BAA4B,+BA+B1G;kBA/BQ,uBAAuB;;;AAmChC,OAAO,EAAE,uBAAuB,EAAE,CAAC;AACnC,YAAY,EAAE,4BAA4B,EAAE,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { LucideIcon } from '../../icons';
2
+ interface EducationalLandingPillarProps {
3
+ /** A Lucide icon from `@gravitee/graphene-core/icons`, drawn in a tinted chip next to the title. */
4
+ readonly icon: LucideIcon;
5
+ /** One or two words: "Routing", "Cost control". Rendered as an `<h3>`. */
6
+ readonly title: string;
7
+ /** One sentence on what the capability does for the user. */
8
+ readonly description: string;
9
+ readonly className?: string;
10
+ }
11
+ /**
12
+ * One key capability of the entity: an icon chip next to its title, then one sentence. A list item
13
+ * of `EducationalLandingPillars`; it holds no link or button.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Steer each request to the right model." />
18
+ * ```
19
+ */
20
+ declare function EducationalLandingPillar({ icon: Icon, title, description, className }: EducationalLandingPillarProps): import("react").JSX.Element;
21
+ declare namespace EducationalLandingPillar {
22
+ var displayName: string;
23
+ }
24
+ export { EducationalLandingPillar };
25
+ export type { EducationalLandingPillarProps };
26
+ //# sourceMappingURL=EducationalLandingPillar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EducationalLandingPillar.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingPillar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,UAAU,6BAA6B;IACrC,oGAAoG;IACpG,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,iBAAS,wBAAwB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,6BAA6B,+BAY7G;kBAZQ,wBAAwB;;;AAgBjC,OAAO,EAAE,wBAAwB,EAAE,CAAC;AACpC,YAAY,EAAE,6BAA6B,EAAE,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { ReactNode } from 'react';
2
+ interface EducationalLandingPillarsProps {
3
+ /**
4
+ * Columns in a wide card, 3 by default; one column in a narrow card, and 4 go through 2. Four
5
+ * pillars in 2 columns make two rows of two.
6
+ */
7
+ readonly columns?: 2 | 3 | 4;
8
+ /** The `EducationalLandingPillar` items. */
9
+ readonly children: ReactNode;
10
+ readonly className?: string;
11
+ }
12
+ /**
13
+ * The key capabilities of the entity, as a list laid out in a grid: usually three
14
+ * `EducationalLandingPillar`, last in the card.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <EducationalLandingPillars>
19
+ * <EducationalLandingPillar icon={WaypointsIcon} title="Routing" description="Steer each request to the right model." />
20
+ * <EducationalLandingPillar icon={BarChart3Icon} title="Cost control" description="Keep LLM spend predictable." />
21
+ * <EducationalLandingPillar icon={ShieldIcon} title="Resilience" description="Fail over when a provider is down." />
22
+ * </EducationalLandingPillars>
23
+ * ```
24
+ */
25
+ declare function EducationalLandingPillars({ columns, children, className }: EducationalLandingPillarsProps): import("react").JSX.Element;
26
+ declare namespace EducationalLandingPillars {
27
+ var displayName: string;
28
+ }
29
+ export { EducationalLandingPillars };
30
+ export type { EducationalLandingPillarsProps };
31
+ //# sourceMappingURL=EducationalLandingPillars.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EducationalLandingPillars.d.ts","sourceRoot":"","sources":["../../../src/composed/EducationalLanding/EducationalLandingPillars.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWvC,UAAU,8BAA8B;IACtC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,yBAAyB,CAAC,EAAE,OAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,8BAA8B,+BAMtG;kBANQ,yBAAyB;;;AAUlC,OAAO,EAAE,yBAAyB,EAAE,CAAC;AACrC,YAAY,EAAE,8BAA8B,EAAE,CAAC"}
@@ -0,0 +1,13 @@
1
+ export { EducationalLanding } from './EducationalLanding';
2
+ export type { EducationalLandingProps } from './EducationalLanding';
3
+ export { EducationalLandingComparison } from './EducationalLandingComparison';
4
+ export type { EducationalLandingComparisonProps } from './EducationalLandingComparison';
5
+ export { EducationalLandingFlow } from './EducationalLandingFlow';
6
+ export type { EducationalLandingFlowProps, EducationalLandingFlowStep } from './EducationalLandingFlow';
7
+ export { EducationalLandingPanel } from './EducationalLandingPanel';
8
+ export type { EducationalLandingPanelProps } from './EducationalLandingPanel';
9
+ export { EducationalLandingPillar } from './EducationalLandingPillar';
10
+ export type { EducationalLandingPillarProps } from './EducationalLandingPillar';
11
+ export { EducationalLandingPillars } from './EducationalLandingPillars';
12
+ export type { EducationalLandingPillarsProps } from './EducationalLandingPillars';
13
+ //# sourceMappingURL=index.d.ts.map