Use this skill for Make App filtering. A keyword-search-only list that sends filter.expression uses the package compiler without enabling advanced filtering. Desktop/tablet lists that use advanced filters, condition builders, table filtering, or CanvasTable header "按该字段筛选" deliver one integrated feature:
- package-backed toolbar advanced filter using
@qfei-design/make-app-filter - host-owned CanvasTable header filter UI/menu
- linkage from header "按该字段筛选" to the same package controller and toolbar panel
- Entity Preset advanced-filter save, load, hydration, and clear behavior
- Service
filter.expressionpayload integration
When advanced filtering is in scope, phones use the same package controller, Preset and Service filter.expression, but expose only the medium-sized filter trigger in the compact phone toolbar and AdvancedFilterPanel layout="mobile" in MobileFilterSheet from @qfei-design/make-app-mobile/primitives. Phone Select controls use MobileFilterSelect from its /pickers entry; keep desktop/tablet controls on the existing UI library. Phone top filtering does not require CanvasTable header linkage, and a hidden CanvasTable must not be mounted to satisfy the desktop contract.
On desktop/tablet, do not implement only advanced filter or only header filter in Make record-list pages. They must be done together or not done.
This skill owns the consumer-side package integration contract, advanced-filter behavior, field support, Entity Preset filter persistence, Service filter payload shape, host-owned header-linkage semantics, URL/deep-link filter echo, and filter-specific tests. It consumes list-access state from make-app-permission; it does not define permission policy. It does not own sorting (make-app-sort), grouping (make-app-group), page shell/layout (makeui), CanvasTable rendering internals or header menu API details (canvas-table-integration), Service route implementation (make-app-service), auth (make-app-auth), runtime packaging (make-app-runtime), DSL modeling (makedsl), or Make CLI execution (makecli).
Capability modes
Choose the mode before following the workflow. Panel, controller, filter Preset and mobile-layout requirements below apply only to advanced-filter unless explicitly stated otherwise.
| Mode | Query | Filter Preset | UI and package gate |
|---|---|---|---|
search-only |
compileListFilter({ fields, searchText }) |
no filter Preset GET/PATCH or hydration | no controller, panel, or mobile layout gate; use the package core compiler |
advanced-filter |
compileListFilter({ fields, searchText, advancedFilter }) |
GET/hydrate before the first records query; PATCH the filter dimension only after confirmation | package controller and panel; phone requires public layout="mobile" |
search-only does not load or apply a saved advanced-filter expression behind an absent filter UI. If sorting or grouping independently requires a shared Entity Preset, its own coordinator may still load that Preset; this Skill does not consume its filter dimension until advanced filtering is enabled. Both modes still obey the list-access gate and send the package compiler result unchanged to Service.
Quick start
- Treat any Make record-list request containing "筛选", "高级筛选", "条件筛选", "表格筛选", "表头筛选", "列头筛选", or "按字段筛选" as the same filtering requirement. Desktop/tablet implement both the package-backed toolbar advanced filter and CanvasTable header linkage. Phone uses the package-backed top filter entry without CanvasTable/header linkage.
- Locate the host UI package, usually
apps/ui/package.json. If no UI package exists, stop and report the missing host package. - Ensure
@qfei-design/make-app-filter@^1.0.0is installed for compiler-only or desktop/tablet use. Phone advanced filtering uses@qfei-design/make-app-filter@^1.1.0together with@qfei-design/make-app-mobile@^0.1.11, after the host compatibility gate. The former supplies 32pxmiddlecontrols and mobile-scoped control classes; the latter suppliesMobileFilterSheet,MobileFilterSelect, and date-triggerclassName. Check the resolved filter package's public React declaration for the"mobile"layout; earlier filter 1.0.0–1.0.3 lack it. Search-only phone lists need only the public core compiler, without either mobile panel gate. Do not substitute default/stacked layout or hand-write package internals. - Read package docs before designing code. Prefer installed package docs; if the host is working in the package repo, read source docs.
- In advanced-filter mode, import
@qfei-design/make-app-filter/styles.cssonce in the host UI entry; search-only compiler use does not need panel styles. - Both modes use the package core compiler. Advanced-filter mode additionally uses package panel, controller, adapter, validation, and CEL parse APIs. Do not copy or hand-write these capabilities in the host.
- In advanced-filter mode, keep presentation-specific toolbar trigger, desktop Popover or phone
MobileFilterSheet, applied state, candidate APIs, Service request adapter, and desktop/tablet CanvasTable header filter UI/menu in the host. The phone panel passeslayout="mobile"and receives a mobile-only component adapter usingMobileFilterSelect; the package Sheet owns the height and scroll boundary. Search-only has no panel surface. In the desktop/tablet component adapter, decorate only the actual “选择字段” options with the default Make field-type icons; operator, value and candidate options remain unchanged. Phone field options remain plain text. - On desktop/tablet, wire header "按该字段筛选" to the same package controller/panel; do not create separate header-only state or a local filter implementation. Phone has no header entry.
- Align with the backend Record list filter contract: Service sends
filter: { expression }, blank expressions mean no filter, and field support must match runtime metadata plus package public APIs. SubmitcompileListFilteroutput unchanged; never rewrite CEL/DNF in the host. - For entity object lists, consume the list-access gate from
make-app-permission. Only when advanced filtering is enabled and list access is allowed, load the current Entity Preset before the first records query and hydrate the saved filter through package public APIs using a permission-aware{ enabled, entityKey, generation }context. Search-only does not load or hydrate the filter Preset, and toolbar search remains session-only. - On filter confirm, PATCH only the Preset
filterdimension. After success, update applied state synchronously; let the records query react to applied state instead of reloading inside the persistence callback. Preserve the old applied filter and current draft on failure. - When the writable list uses
make-app-actions, hand off the successfully applied filter generation so actions clear selection and invalidate pending precheck/submit work before the new query is actionable. Draft edits, cancel, and save/apply failure preserve the current action selection. - In advanced-filter mode, preserve the required fixed three-region layout: top fixed header, scrollable condition body, and bottom fixed footer. Header/footer controls must remain visible while condition rows scroll.
- Before finishing, verify package compilation, empty filter omission, Service payload shape and list-access behavior in both modes. For search-only, verify no filter Preset GET/PATCH, hydration, panel or mobile-layout gate. For advanced filtering, also verify fixed panel layout, search merge, Preset save/load/clear, draft confirm/discard, candidates, desktop/tablet header linkage, phone no-CanvasTable behavior, nested picker stacking, 32px panel controls, selected-value echo during remote search, package/backend field-support drift and conditional action-selection invalidation.
Package pre-flight
New Make Apps and Apps undergoing an explicit runtime migration use the make-app-runtime runtime contract: the workspace root declares "packageManager": "pnpm@10.20.0" plus "engines": { "node": "22.20.0", "pnpm": "10.20.0" }, and package operations run through corepack pnpm. For those Apps, install the dependency in the actual UI package, for example corepack pnpm add @qfei-design/make-app-filter@^1.0.0 from its directory; a new App with no lockfile first needs the runtime declaration and a generated pnpm workspace.
Existing Make Apps retain their declared runtime during filter work. Before installing or upgrading, locate the UI package and workspace root, inspect packageManager, Node engines, React/React DOM peers, the existing lockfile, and the repository's established install/CI workflow. Use the matching existing manager and workspace target:
pnpm-lock.yamlwith a compatible pnpm declaration/workflow: use that project's declared pnpm version through Corepack to add@qfei-design/make-app-filter@^1.0.0to the UI package.package-lock.jsonwith a compatible npm declaration/workflow: use the existing npm workspace install command targeting the UI package, or install from that package directory if the repository is not a workspace.yarn.lockwith a compatible Yarn declaration/workflow: use the existing Yarn workspace add command targeting the UI package, or add from that package directory if the repository is not a workspace. Preserve the project's Yarn version and configuration.
Do not rewrite an existing App's packageManager, Node selection, workspace layout, or lockfile type merely to add filtering. Never generate a second lockfile. If the declaration, lockfile, CI workflow, Node/peer constraints, or target workspace conflict—or an existing App has no unambiguous install workflow—stop and report a compatibility blocker to make-app-runtime; do not silently migrate it or force corepack pnpm into an npm/Yarn project. The phone filter package upgrade follows the same gate.
For phone advanced filtering, use that same existing manager and target UI package to install @qfei-design/make-app-filter@^1.1.0 and @qfei-design/make-app-mobile@^0.1.11 together. The ^1.0.0 examples above remain the general compiler/desktop baseline. Check the versions actually resolved by the lockfile; a declared range alone is not proof that both phone capabilities are installed.
Migrate any retired pre-1.0 package dependency to
@qfei-design/make-app-filter@^1.0.0 and update public imports together. If an
unrelated advanced-filter package is already used, stop and ask before replacing
it. Do not fall back to a pre-1.0 release.
Required read procedure for installed 1.0.0+ packages:
node_modules/@qfei-design/make-app-filter/package.ai.json- Parse
package.ai.json.readOrderand resolve every entry relative tonode_modules/@qfei-design/make-app-filter. - Verify each referenced file exists in the installed package before relying on it.
- Read the remaining entries in the declared order, skipping the already-read
package.ai.jsonentry.
package.ai.json.readOrder is the source of truth. Do not hardcode docs/, examples/, or other package-internal documentation paths. When working directly in the package repo, resolve the same entries from the repository root. If the installed package is older than 1.0.0, upgrade first instead of relying on older package docs or inferred internals.
Topic reference map
| Task / topic | Read |
|---|---|
Keyword search through filter.expression without advanced filtering |
references/service-translation.md; use the compiler-only path in references/filter-model.md |
| Package install, imports, host/package boundary | references/package-integration.md |
| Filter IR, controller draft/confirm semantics, search merge, URL echo | references/filter-model.md |
| Runtime field capability, operator/value-editor APIs, and candidate values | references/operator-matrix.md |
| Host Popover/container, trigger, panel sizing, validation visuals | references/ui-style.md |
| Default desktop/tablet field-choice icons in the filter panel | makeui field-type icon contract |
| CanvasTable header more menu and advanced filter linkage | references/header-table-linkage.md |
| Service filter contract and CEL expression payload | references/service-translation.md |
| Advanced-filter Entity Preset load, hydration, save barrier, clear, stale requests | references/preset-integration.md |
| Tests, smoke checks, common regressions | references/testing-and-pitfalls.md |
| Backend Record filter contract, CEL subset, DateRange/File/Lookup/system variables | Use makedsl; read its EntityDataFilterUsage reference |
Group path expression composition and record-groups groupFilter |
Use make-app-group; reuse this Skill's DNF expression rules |
| Toolbar placement and surrounding page layout | Use makeui |
CanvasTable suffixRender mechanics |
Use canvas-table-integration |
| Service route implementation and adapter tests | Use make-app-service |
| Writable-list selection actions and applied-query invalidation | Use make-app-actions |
Hard rules
- Do not create new Make advanced-filter implementations in host apps. No hand-written Filter IR helpers, operator matrix, validator, CEL compiler/parser, or advanced filter panel when the package provides it.
- Do not deliver advanced filtering partially. Both modes use the package compiler and Service expression payload; advanced-filter mode additionally uses the package controller. Desktop/tablet advanced filtering implements CanvasTable header UI and linkage. Phone advanced filtering uses only its top toolbar filter entry and mobile-safe panel; it does not require or render CanvasTable header linkage.
- Phone advanced filtering requires the public
"mobile"panel layout,MobileFilterSheet,MobileFilterSelect, andAdvancedFilterPanel layout="mobile"at the resolved versions above. A desktop Popover on a narrow viewport,layout="default", or CSS-shrinking its condition rows is not mobile adaptation. Keep desktop/tablet Popover wiring and layout rules, then check their rendered controls after the shared filter package upgrade. Search-only does not require the mobile panel layout or mobile package filter components. - New integrations use the core API from
@qfei-design/make-app-filter; advanced-filter integrations additionally use@qfei-design/make-app-filter/reactand optional@qfei-design/make-app-filter/adapters/antd. - Advanced-filter integrations import
@qfei-design/make-app-filter/styles.cssonce. Search-only compiler use does not require panel styles. Host CSS may style the outer overlay/container, but must not fork package internals unless fixing a host-specific containment issue. - New filter output uses
filter: { expression: string }. IfcompileListFilterreturnsundefined, omitfilter. compileListFilteris the only host-facing search/advanced-filter compiler. Send its result unchanged; do not parse, redistribute, or rewrite CEL/DNF in host code.- The backend Record list handler reads only
filter.expressionfrom theExpressionobject and treats missing,null, or blank expressions as no filter. - Do not send
filter: [],filter: {},{ expression: "" }, blank raw filter strings, or old object-array DSL. - Do not filter Make record lists locally. List filtering goes through Service/backend filter APIs.
- Filter fields come from normalized runtime object/field metadata. Do not read
apps/dsl/**, copied YAML, row samples, or hardcoded demo data as runtime filter metadata. - A host field-type registry may help normalize shared runtime metadata, but it must not decide filter operators or value editors. Pass fields to the package and use its capability APIs as the filter source of truth.
- For Lookup filtering, resolve
relationKey, the opposite Entity, andtargetFieldKeyfrom the complete runtime schema before passing field metadata to the package. Keep the source Lookup field key in Filter IR and CEL expressions; target field metadata only controls operators, values, and validation. - User and department filter values are identities, not display names. Candidate sources come from the host contract owned by
makeui/make-app-service; do not define transport routes in this Skill. - Do not source user/department options from field schema
options, current table rows, local arrays, or display labels. Current applied values may be merged only to keep labels visible while remote candidates load. - Backend Record filters support DateRange, File, and Lookup semantics, but the UI may expose a field only when
@qfei-design/make-app-filterpublic APIs support that field/operator combination. If backend docs and package capabilities differ, stop to upgrade/fix the package or report the mismatch; do not hand-write CEL or guess package internals. - In advanced-filter mode, on every entity or permission-enabled context change, increment a monotonic request generation and reset the host panel state. Comparing only
entityKeywhen a save settles is unsafe because an oldA -> B -> Aresult or a result from before access revocation can look current. Search-only still invalidates stale record-query responses without creating panel state. - In advanced-filter mode, use a committed-context reset such as a keyed wrapper plus layout-effect cleanup. Do not mutate request-generation, saving, or context refs during React render.
- In advanced-filter mode, if saved CEL is unsupported by the current package, keep its raw expression active in backend requests, keep the trigger visibly active, and show a compatibility warning until an explicit replacement or clear saves successfully.
- In advanced-filter mode, unsupported package fields must be hidden from desktop/tablet and phone field selectors and from desktop/tablet header "按该字段筛选"; do not call
openWithFieldfor unknown fields, invalid field keys, or package-unsupported field/operator combinations. - On desktop/tablet, Header menu filtering is a host integration. It appends a draft condition through the package controller and opens the same toolbar filter panel. It must not submit immediately, reload records, or create a separate header-only state; phone has no header menu.
- On desktop/tablet, Table scrolling, object switching, outside click, or unmount must close any header menu and restore the header suffix icon to hover-only state.
- Advanced filter panel layout is mandatory: every Make advanced filter must use the fixed three-region baseline with a fixed header, scrollable body, and fixed footer. A panel where add/confirm/clear actions scroll away with conditions is a readiness blocker and must not be reported as ready, complete, or delivered.
- Only when advanced filtering is enabled, persist its expression in the current user's Entity Preset and load and hydrate the Preset filter before the first records query. Search-only does not read, hydrate or write the filter Preset.
- Preset writes are sparse. Saving filter sends only
{ filter }, never a possibly stalesortorgroup. - Save before apply. Preset save failure keeps the previous applied filter, open panel, and current draft; it must not reload records.
- If
make-app-actionsis present, only a successfully applied filter generation clears its selection and invalidates pending action work. Draft edits, panel cancel, validation failure, Preset save failure, and failed list queries do not clear or redefine the current action selection. - Clear advanced filter with
filter: null. Toolbar keyword search remains session-only and must not be persisted. - When list access is disabled, block new schema and records requests and invalidate in-flight results. In advanced-filter mode, also block Preset GET/PATCH, close filter surfaces and ignore stale Preset load/save responses after either
entityKeyor permission-enabled state changes. Search-only has no filter Preset request or surface to close.
Default behavior
- Advanced filtering UI is an optional product capability in every presentation mode. Generate its trigger and panel only when requested or already established by the project; enabling the standard phone View alone does not add them. A search-only list that sends
filter.expressionusescompileListFilter({ fields, searchText })without a filter trigger, panel, filter Preset GET/PATCH or hydration; omitadvancedFilterrather than passing an explicit undefined value. When advanced filtering is in scope, adapt it on the phone as well as desktop/tablet rather than leaving a nonfunctional phone trigger. - Once advanced filtering is in scope, use the package-backed controller,
AdvancedFilterPanel, draft lifecycle, fixed header/body/footer, field controls and Servicefilter.expression. Desktop/tablet use the toolbar trigger, host popover and CanvasTable headeropenWithFieldlinkage. Phone/mobile uses the top toolbar 筛选 entry and packagelayout="mobile"inMobileFilterSheet; it does not need CanvasTable, header, or header linkage. - On desktop/tablet, the advanced filter panel must keep its three regions explicit: header top fixed with left
筛选and right清空所有, body middle containing only condition rows/groups and using the only vertical scroll, footer bottom fixed with left+ 添加条件and+ 添加条件组and right确认. On phone, retain the fixed three regions but use the packagelayout="mobile"defaults (设置筛选条件and完成) insideMobileFilterSheet; do not force desktop labels or inline-row styling onto that mode. - The host keeps search text separate from advanced filter state. Search-only compiles
compileListFilter({ fields, searchText }); advanced-filter mode combines applied filter and search throughcompileListFilter({ fields, searchText, advancedFilter }). - Closing the owning desktop/tablet Popover or phone Sheet without confirmation discards unconfirmed draft changes through the package controller reset flow.
- The clear action changes only the draft; it affects the applied filter only after the active mode's confirm action (
确认on desktop/tablet,完成on phone) succeeds. - Object/entity or permission-enabled context changes clear transient search state and invalidate old requests. In advanced-filter mode, also clear filter/panel state and reload the Entity Preset only when access is enabled; clear header state and reset desktop/tablet table object-level transient state when mounted. Phone has no header filter UI. Search-only does not reload the filter Preset.
Collaboration rules
- With
makeui: usemakeuifor the desktop/tablet toolbar and the separate phone top filter entry, page shell, surrounding layout, and candidate-source UI contract; this skill owns filter behavior and package integration. - With
canvas-table-integration: use that skill for CanvasTablesuffixRenderand header menu mechanics; this skill owns how the host "按该字段筛选" action talks to the package-backed advanced-filter controller. - With
make-app-service: this skill defines filter query and Preset filter semantics; Service route validation, adapter logging, and Make request details stay in service. - With
make-app-sort: when advanced filtering is enabled, filter and sort share one parent-owned Entity Preset coordinator and load lifecycle but update their dimensions independently. Search-only does not consume the Preset filter dimension. This skill does not define sorting UI or sort validation. - With
make-app-group: filter and group share expression syntax; when advanced filtering is enabled they share the Preset lifecycle but update dimensions independently. This skill owns globalfilter.expression;make-app-groupowns transient pathgroupFiltercomposition and record-groups timing. - With
make-app-permission: consume the resolved list-access gate in both capability modes; advanced filtering also includes its enabled state in the Preset request generation. This skill does not define permission policy or permission endpoints. - With
make-app-actions: only for writable lists using the action workflow, hand off a successfully applied filter generation before the new query becomes actionable so actions clear selection and invalidate pending work. Draft and failure paths preserve selection; this Skill does not manipulate CanvasTable selection APIs directly. - With
makedsl: readEntityDataFilterUsage.mdto confirm backend filter semantics such as DNF, system variables, DateRange/File/Lookup, empty filter handling, and error cases. Do not generate DSL from this skill.