@humanforest/nuxt-layer 0.1.0
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.
- package/LICENSE +67 -0
- package/README.md +208 -0
- package/app.config.ts +10 -0
- package/assets/icons/forest/bike-asterisk.svg +21 -0
- package/assets/icons/forest/bike-down.svg +19 -0
- package/assets/icons/forest/bike-off.svg +20 -0
- package/assets/icons/forest/bike-up.svg +19 -0
- package/assets/icons/forest/bike-x.svg +19 -0
- package/assets/icons/forest/bike.svg +18 -0
- package/assets/icons/forest/mark-fill.svg +6 -0
- package/assets/icons/forest/mark.svg +6 -0
- package/assets/icons/forest/parking.svg +3 -0
- package/assets/icons/forest/star-fill.svg +4 -0
- package/assets/icons/forest/star.svg +4 -0
- package/assets/icons/forest/traffic-light-caution.svg +8 -0
- package/assets/icons/forest/traffic-light-go.svg +8 -0
- package/assets/icons/forest/traffic-light-lit.svg +8 -0
- package/assets/icons/forest/traffic-light-stop.svg +8 -0
- package/assets/icons/forest/traffic-light.svg +8 -0
- package/nuxt.config.ts +81 -0
- package/package.json +35 -0
- package/packages/charts/src/FBoroughShape.vue +52 -0
- package/packages/charts/src/FCalendarGrid.vue +332 -0
- package/packages/charts/src/FCellLegend.vue +54 -0
- package/packages/charts/src/FChartFrame.vue +201 -0
- package/packages/charts/src/FChartFrameSingle.vue +91 -0
- package/packages/charts/src/FChartLegend.vue +26 -0
- package/packages/charts/src/FDistributionBar.vue +224 -0
- package/packages/charts/src/FDottedMap.vue +335 -0
- package/packages/charts/src/FLondonMap.vue +399 -0
- package/packages/charts/src/FSparkline.vue +198 -0
- package/packages/charts/src/FStatusTrack.vue +151 -0
- package/packages/charts/src/cellSize.ts +46 -0
- package/packages/charts/src/distribution.ts +99 -0
- package/packages/charts/src/engine.ts +15 -0
- package/packages/charts/src/forestTooltip.ts +110 -0
- package/packages/charts/src/fromCategories.ts +43 -0
- package/packages/charts/src/index.ts +87 -0
- package/packages/charts/src/londonAreas.ts +75 -0
- package/packages/charts/src/londonGrid.ts +67 -0
- package/packages/charts/src/motionDuration.ts +49 -0
- package/packages/charts/src/presets.ts +143 -0
- package/packages/charts/src/resolveVar.ts +21 -0
- package/packages/charts/src/snapTooltip.ts +154 -0
- package/packages/charts/src/tooltipStandIn.ts +58 -0
- package/packages/charts/src/useCellScale.ts +155 -0
- package/packages/charts/src/useChartPalette.ts +61 -0
- package/packages/charts/src/useChartRepaintKey.ts +22 -0
- package/packages/charts/src/useThemeVersion.ts +17 -0
- package/packages/frames/src/FBrowserFrame.vue +53 -0
- package/packages/frames/src/FDeviceFrame.vue +242 -0
- package/packages/frames/src/FFitFrame.vue +83 -0
- package/packages/frames/src/art/iphone-11.svg +124 -0
- package/packages/frames/src/art/iphone-12.svg +601 -0
- package/packages/frames/src/art/iphone-13-pro.svg +820 -0
- package/packages/frames/src/art/iphone-13.svg +849 -0
- package/packages/frames/src/art/iphone-14.svg +681 -0
- package/packages/frames/src/art/iphone-15-pro.svg +633 -0
- package/packages/frames/src/art/iphone-15.svg +713 -0
- package/packages/frames/src/art/iphone-16-plus.svg +685 -0
- package/packages/frames/src/art/iphone-16-pro-max.svg +611 -0
- package/packages/frames/src/art/iphone-16-pro.svg +631 -0
- package/packages/frames/src/art/iphone-16.svg +713 -0
- package/packages/frames/src/art/iphone-17-pro-max.svg +355 -0
- package/packages/frames/src/art/iphone-17.svg +305 -0
- package/packages/frames/src/art/iphone-air.svg +389 -0
- package/packages/frames/src/art/iphone-se-3.svg +473 -0
- package/packages/frames/src/art/iphone-x.svg +652 -0
- package/packages/frames/src/art/pixel-10-pro-xl.svg +32 -0
- package/packages/frames/src/art/pixel-10-pro.svg +32 -0
- package/packages/frames/src/art/pixel-10.svg +32 -0
- package/packages/frames/src/art/pixel-8-pro.svg +208 -0
- package/packages/frames/src/art/pixel-8.svg +208 -0
- package/packages/frames/src/art/pixel-8a.svg +8 -0
- package/packages/frames/src/art/pixel-9-pro-xl.svg +32 -0
- package/packages/frames/src/art/pixel-9-pro.svg +34 -0
- package/packages/frames/src/art/pixel-9a.svg +25 -0
- package/packages/frames/src/art.ts +29 -0
- package/packages/frames/src/devices.ts +337 -0
- package/packages/frames/src/index.ts +15 -0
- package/packages/tokens/geo/london-boroughs.json +1 -0
- package/packages/tokens/geo/london-dots-coarse.json +1 -0
- package/packages/tokens/geo/london-dots-fine.json +1 -0
- package/packages/tokens/geo/london-dots-medium.json +1 -0
- package/packages/tokens/geo/london-dots-ultra.json +1 -0
- package/packages/tokens/geo/london-wards.json +1 -0
- package/packages/tokens/glyphs/glyphs.json +1 -0
- package/packages/tokens/logos/f-rot.svg +4 -0
- package/packages/tokens/logos/f.svg +4 -0
- package/packages/tokens/logos/favicon-c2w.svg +9 -0
- package/packages/tokens/logos/favicon-console.svg +9 -0
- package/packages/tokens/logos/favicon-ds.svg +9 -0
- package/packages/tokens/logos/favicon-fleet.svg +9 -0
- package/packages/tokens/logos/favicon-flex.svg +9 -0
- package/packages/tokens/logos/favicon-plus.svg +9 -0
- package/packages/tokens/logos/favicon-radar.svg +9 -0
- package/packages/tokens/logos/favicon-tower.svg +9 -0
- package/packages/tokens/logos/lockup-horizontal.svg +17 -0
- package/packages/tokens/logos/lockup-vertical.svg +17 -0
- package/packages/tokens/logos/mark-mono.svg +4 -0
- package/packages/tokens/logos/mark.svg +7 -0
- package/packages/tokens/logos/wordmark.svg +9 -0
- package/packages/tokens/src/colourEngine.ts +604 -0
- package/packages/tokens/src/glyphs.ts +127 -0
- package/packages/tokens/src/logo.ts +50 -0
- package/packages/tokens/src/subbrands.ts +44 -0
- package/packages/tokens/src/sublogo.ts +80 -0
- package/packages/ui/icons/forest/map/forest-place-count.svg +21 -0
- package/packages/ui/icons/forest/map/forest-place-marker.svg +17 -0
- package/packages/ui/icons/forest/map/forest-vehicle-marker.svg +73 -0
- package/packages/ui/icons/forest/map/manifest.json +373 -0
- package/packages/ui/icons/forest/map/place-count-manifest.json +83 -0
- package/packages/ui/icons/forest/map/place-manifest.json +74 -0
- package/packages/ui/src/address/address.ts +125 -0
- package/packages/ui/src/address/autofill.ts +78 -0
- package/packages/ui/src/address/geocode.ts +55 -0
- package/packages/ui/src/address/index.ts +31 -0
- package/packages/ui/src/address/validate.ts +91 -0
- package/packages/ui/src/canvas/FCanvasCard.vue +97 -0
- package/packages/ui/src/canvas/FCanvasControl.vue +121 -0
- package/packages/ui/src/canvas/FCanvasControlGroup.vue +100 -0
- package/packages/ui/src/canvas/FCanvasFlyout.vue +216 -0
- package/packages/ui/src/canvas/FCanvasHud.vue +402 -0
- package/packages/ui/src/canvas/canvasSurface.ts +39 -0
- package/packages/ui/src/canvas/index.ts +17 -0
- package/packages/ui/src/contexts/ForestContext.vue +53 -0
- package/packages/ui/src/contexts/contexts.ts +153 -0
- package/packages/ui/src/contexts/index.ts +23 -0
- package/packages/ui/src/contexts/marketing-theme.ts +214 -0
- package/packages/ui/src/contexts/mobile-theme.ts +402 -0
- package/packages/ui/src/filter/FFilterBar.vue +72 -0
- package/packages/ui/src/filter/FFilterChip.vue +362 -0
- package/packages/ui/src/filter/FFilterClauses.vue +104 -0
- package/packages/ui/src/filter/FFilterTrigger.vue +183 -0
- package/packages/ui/src/filter/filter.theme.ts +27 -0
- package/packages/ui/src/filter/filterClause.ts +265 -0
- package/packages/ui/src/filter/filterPredicate.ts +134 -0
- package/packages/ui/src/filter/filterSchema.ts +82 -0
- package/packages/ui/src/filter/index.ts +36 -0
- package/packages/ui/src/forest-preset.ts +68 -0
- package/packages/ui/src/icons.ts +79 -0
- package/packages/ui/src/index.ts +17 -0
- package/packages/ui/src/kpi/FKpi.vue +253 -0
- package/packages/ui/src/kpi/FKpiGroup.vue +161 -0
- package/packages/ui/src/kpi/index.ts +20 -0
- package/packages/ui/src/kpi/kpi.theme.ts +168 -0
- package/packages/ui/src/kpi/kpiDelta.ts +82 -0
- package/packages/ui/src/kpi/kpiFormat.ts +72 -0
- package/packages/ui/src/kpi/kpiGroupState.ts +63 -0
- package/packages/ui/src/logo/FLogo.vue +52 -0
- package/packages/ui/src/logo/FSubLogo.vue +214 -0
- package/packages/ui/src/logo/index.ts +8 -0
- package/packages/ui/src/map/FMap.vue +355 -0
- package/packages/ui/src/map/FMapCompass.vue +154 -0
- package/packages/ui/src/map/FMapCoordinates.vue +134 -0
- package/packages/ui/src/map/FMapDrawTools.vue +116 -0
- package/packages/ui/src/map/FMapFullscreen.vue +92 -0
- package/packages/ui/src/map/FMapGeolocate.vue +208 -0
- package/packages/ui/src/map/FMapLayers.vue +75 -0
- package/packages/ui/src/map/FMapLegend.vue +45 -0
- package/packages/ui/src/map/FMapPopup.vue +129 -0
- package/packages/ui/src/map/FMapRamp.vue +69 -0
- package/packages/ui/src/map/FMapRegion.vue +77 -0
- package/packages/ui/src/map/FMapScale.vue +107 -0
- package/packages/ui/src/map/FMapSearch.vue +362 -0
- package/packages/ui/src/map/FMapTilt.vue +87 -0
- package/packages/ui/src/map/FMapTools.vue +61 -0
- package/packages/ui/src/map/FMapZoom.vue +81 -0
- package/packages/ui/src/map/FMarker.vue +218 -0
- package/packages/ui/src/map/FTrackScrubber.vue +94 -0
- package/packages/ui/src/map/FVehicleLegend.vue +72 -0
- package/packages/ui/src/map/FVehicleMark.vue +166 -0
- package/packages/ui/src/map/bin.ts +196 -0
- package/packages/ui/src/map/coordinates.ts +83 -0
- package/packages/ui/src/map/density.ts +401 -0
- package/packages/ui/src/map/draw.ts +356 -0
- package/packages/ui/src/map/ease.ts +67 -0
- package/packages/ui/src/map/fullscreen.ts +59 -0
- package/packages/ui/src/map/heat.ts +160 -0
- package/packages/ui/src/map/index.ts +351 -0
- package/packages/ui/src/map/liveMarkers.ts +60 -0
- package/packages/ui/src/map/map.css +116 -0
- package/packages/ui/src/map/mapColor.ts +31 -0
- package/packages/ui/src/map/mapContext.ts +12 -0
- package/packages/ui/src/map/mapLayers.ts +55 -0
- package/packages/ui/src/map/mapLegend.ts +70 -0
- package/packages/ui/src/map/mapSearch.theme.ts +79 -0
- package/packages/ui/src/map/mapSearch.ts +279 -0
- package/packages/ui/src/map/mapUrl.ts +117 -0
- package/packages/ui/src/map/markerAsset.ts +3 -0
- package/packages/ui/src/map/padding.ts +39 -0
- package/packages/ui/src/map/place.ts +192 -0
- package/packages/ui/src/map/ramp.ts +95 -0
- package/packages/ui/src/map/regionSelect.ts +163 -0
- package/packages/ui/src/map/route.ts +167 -0
- package/packages/ui/src/map/scale.ts +329 -0
- package/packages/ui/src/map/scaleBar.ts +67 -0
- package/packages/ui/src/map/selectionModel.ts +50 -0
- package/packages/ui/src/map/sources/coordinates.ts +58 -0
- package/packages/ui/src/map/sources/mapboxPlaces.ts +71 -0
- package/packages/ui/src/map/style.ts +14 -0
- package/packages/ui/src/map/track.ts +279 -0
- package/packages/ui/src/map/useFeatureSelect.ts +230 -0
- package/packages/ui/src/map/useLiveMarkers.ts +84 -0
- package/packages/ui/src/map/useMap.ts +150 -0
- package/packages/ui/src/map/useMapDraw.ts +776 -0
- package/packages/ui/src/map/useMapOverlay.ts +421 -0
- package/packages/ui/src/map/useMapTableSelection.ts +101 -0
- package/packages/ui/src/map/useMapUrlState.ts +155 -0
- package/packages/ui/src/map/useMarkerSource.ts +163 -0
- package/packages/ui/src/map/useRegionSelect.ts +309 -0
- package/packages/ui/src/map/useTokenMode.ts +61 -0
- package/packages/ui/src/map/useTrackPlayback.ts +131 -0
- package/packages/ui/src/map/useVehicleFilter.ts +51 -0
- package/packages/ui/src/map/useVisibleFeatures.ts +124 -0
- package/packages/ui/src/map/vehicle.ts +807 -0
- package/packages/ui/src/map/vehicleFilter.ts +284 -0
- package/packages/ui/src/map/vehicleLegend.ts +155 -0
- package/packages/ui/src/map/visibleFeatures.ts +192 -0
- package/packages/ui/src/map/zone.ts +291 -0
- package/packages/ui/src/shell/FAppNavbar.vue +68 -0
- package/packages/ui/src/shell/FAppShell.vue +163 -0
- package/packages/ui/src/shell/FAppSidebar.vue +215 -0
- package/packages/ui/src/shell/FPageToolbar.vue +126 -0
- package/packages/ui/src/shell/FScrollShadow.vue +43 -0
- package/packages/ui/src/shell/FSidePanel.vue +258 -0
- package/packages/ui/src/shell/FToolbarActions.vue +32 -0
- package/packages/ui/src/shell/index.ts +33 -0
- package/packages/ui/src/shell/nav.ts +37 -0
- package/packages/ui/src/shell/pageToolbar.ts +69 -0
- package/packages/ui/src/shell/shell.ts +74 -0
- package/packages/ui/src/shell/sidePanel.ts +115 -0
- package/packages/ui/src/themes/accordion.ts +36 -0
- package/packages/ui/src/themes/alert.ts +16 -0
- package/packages/ui/src/themes/auth-form.ts +19 -0
- package/packages/ui/src/themes/avatar-group.ts +9 -0
- package/packages/ui/src/themes/avatar.ts +18 -0
- package/packages/ui/src/themes/badge.ts +51 -0
- package/packages/ui/src/themes/banner.ts +21 -0
- package/packages/ui/src/themes/breadcrumb.ts +10 -0
- package/packages/ui/src/themes/button.mechanics.ts +191 -0
- package/packages/ui/src/themes/button.ts +136 -0
- package/packages/ui/src/themes/calendar.ts +29 -0
- package/packages/ui/src/themes/card.ts +67 -0
- package/packages/ui/src/themes/carousel.ts +41 -0
- package/packages/ui/src/themes/checkbox-group.ts +20 -0
- package/packages/ui/src/themes/checkbox.ts +47 -0
- package/packages/ui/src/themes/chip.ts +50 -0
- package/packages/ui/src/themes/collapsible.ts +26 -0
- package/packages/ui/src/themes/color-picker.ts +13 -0
- package/packages/ui/src/themes/command-palette.ts +37 -0
- package/packages/ui/src/themes/container.ts +14 -0
- package/packages/ui/src/themes/context-menu.ts +38 -0
- package/packages/ui/src/themes/dashboard-group.ts +11 -0
- package/packages/ui/src/themes/dashboard-navbar.ts +10 -0
- package/packages/ui/src/themes/dashboard-panel.ts +18 -0
- package/packages/ui/src/themes/dashboard-resize-handle.ts +35 -0
- package/packages/ui/src/themes/dashboard-sidebar-collapse.ts +7 -0
- package/packages/ui/src/themes/dashboard-sidebar-toggle.ts +7 -0
- package/packages/ui/src/themes/dashboard-sidebar.ts +58 -0
- package/packages/ui/src/themes/dashboard-toolbar.ts +15 -0
- package/packages/ui/src/themes/drawer.ts +45 -0
- package/packages/ui/src/themes/dropdown-menu.ts +38 -0
- package/packages/ui/src/themes/empty.ts +43 -0
- package/packages/ui/src/themes/field-group.ts +52 -0
- package/packages/ui/src/themes/file-upload.ts +38 -0
- package/packages/ui/src/themes/footer-columns.ts +13 -0
- package/packages/ui/src/themes/footer.ts +9 -0
- package/packages/ui/src/themes/form-field.ts +25 -0
- package/packages/ui/src/themes/form.ts +22 -0
- package/packages/ui/src/themes/header.ts +12 -0
- package/packages/ui/src/themes/icon.ts +61 -0
- package/packages/ui/src/themes/index.ts +99 -0
- package/packages/ui/src/themes/input-date.ts +73 -0
- package/packages/ui/src/themes/input-menu.ts +66 -0
- package/packages/ui/src/themes/input-number.ts +58 -0
- package/packages/ui/src/themes/input-rating.ts +25 -0
- package/packages/ui/src/themes/input-tags.ts +63 -0
- package/packages/ui/src/themes/input-time.ts +70 -0
- package/packages/ui/src/themes/input.ts +148 -0
- package/packages/ui/src/themes/kbd.ts +27 -0
- package/packages/ui/src/themes/link.ts +27 -0
- package/packages/ui/src/themes/listbox.ts +48 -0
- package/packages/ui/src/themes/main.ts +6 -0
- package/packages/ui/src/themes/marquee.ts +45 -0
- package/packages/ui/src/themes/modal.ts +46 -0
- package/packages/ui/src/themes/navigation-menu.ts +80 -0
- package/packages/ui/src/themes/page-anchors.ts +7 -0
- package/packages/ui/src/themes/page-aside.ts +7 -0
- package/packages/ui/src/themes/page-card.ts +22 -0
- package/packages/ui/src/themes/page-cta.ts +18 -0
- package/packages/ui/src/themes/page-feature.ts +13 -0
- package/packages/ui/src/themes/page-header.ts +16 -0
- package/packages/ui/src/themes/page-hero.ts +15 -0
- package/packages/ui/src/themes/page-links.ts +12 -0
- package/packages/ui/src/themes/page-section.ts +14 -0
- package/packages/ui/src/themes/page.ts +7 -0
- package/packages/ui/src/themes/pagination.ts +17 -0
- package/packages/ui/src/themes/pin-input.ts +73 -0
- package/packages/ui/src/themes/popover.ts +30 -0
- package/packages/ui/src/themes/progress.ts +29 -0
- package/packages/ui/src/themes/prose.ts +45 -0
- package/packages/ui/src/themes/radio-group.ts +39 -0
- package/packages/ui/src/themes/scroll-area.ts +23 -0
- package/packages/ui/src/themes/select-menu.ts +71 -0
- package/packages/ui/src/themes/select.ts +72 -0
- package/packages/ui/src/themes/separator.ts +28 -0
- package/packages/ui/src/themes/sidebar.ts +127 -0
- package/packages/ui/src/themes/skeleton.ts +15 -0
- package/packages/ui/src/themes/slideover.ts +87 -0
- package/packages/ui/src/themes/slider.ts +48 -0
- package/packages/ui/src/themes/stepper.ts +41 -0
- package/packages/ui/src/themes/switch.ts +63 -0
- package/packages/ui/src/themes/table.ts +34 -0
- package/packages/ui/src/themes/tabs.ts +74 -0
- package/packages/ui/src/themes/textarea.ts +77 -0
- package/packages/ui/src/themes/timeline.ts +25 -0
- package/packages/ui/src/themes/toast.ts +29 -0
- package/packages/ui/src/themes/toaster.ts +66 -0
- package/packages/ui/src/themes/tooltip.ts +31 -0
- package/packages/ui/src/themes/tree.ts +41 -0
- package/packages/ui/src/themes/user.ts +28 -0
- package/styles.cdn.gen.css +2047 -0
- package/theme.gen.json +1 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forest MOBILE platform override for Nuxt UI.
|
|
3
|
+
*
|
|
4
|
+
* `forestPreset` is the canonical (web) theme; this is the DELTA that turns it into the bold mobile
|
|
5
|
+
* cut. Feed it to a root `<UTheme :ui="forestMobileTheme">` (or app.config) in a forest-mobile app —
|
|
6
|
+
* the same shape the showcase uses for its mobile lens. It carries the button deltas plus the
|
|
7
|
+
* rounder card/input cut; grow it as the mobile expression fills in.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: the `ui` prop is slot-level (not per-variant), so `button.base` applies to ALL button variants
|
|
10
|
+
* in the wrapped subtree — mobile reads rounder across the board (on-brand); the deeper-lip vars
|
|
11
|
+
* (--btn-y…) only do anything on the keycap variant, which is the one that translates.
|
|
12
|
+
*
|
|
13
|
+
* Button cut: full pill, 2px edge (--btn-edge), deeper anchored lip (--btn-y*), Mohr-BlackIt display
|
|
14
|
+
* label (always italic + uppercase). NO hover states: touch has no hover (Tailwind v4 already gates
|
|
15
|
+
* `hover:` behind `@media (hover:hover)` on real devices) — and the lens preview on desktop must match,
|
|
16
|
+
* so the hover vars are pinned to their rest values: hover lift == rest lift (--btn-y-h/--btn-yhi) and
|
|
17
|
+
* hover face == rest face (--btn-hover; declared plain AND `dark:` — the PAD trap — because the colour
|
|
18
|
+
* PALs set `dark:[--btn-hover:…]`, which outranks a plain override in dark mode). Press (:active)
|
|
19
|
+
* feedback is kept — touch fires it. Focus is a POP, not a ring: with no hover to lean on, a focused
|
|
20
|
+
* keycap lifts past its rest height (-7px vs the -4px rest lip) and the inherited ::before focus ring is
|
|
21
|
+
* switched off (outline-0!) so the raise is the sole indicator. Mobile-only — the web preset's focus
|
|
22
|
+
* stays ring-on-::before with no lift. Mohr's caps are centred in their own box by the @font-face metric overrides in
|
|
23
|
+
* packages/fonts/forest-fonts.css, so the label needs no box-level treatment and button text may be passed
|
|
24
|
+
* either as the `label` prop or as default-slot children.
|
|
25
|
+
*/
|
|
26
|
+
import type { ContextManifest } from './contexts';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The mobile field edge, shared by every control that can join a UFieldGroup — a mismatched ring
|
|
30
|
+
* along a joined seam is what this constant exists to prevent. `ring-2` matches the button's
|
|
31
|
+
* --btn-edge on this cut. The depth line is drawn from the top edge inward and the ring paints over
|
|
32
|
+
* its first pixels, so --forest-field-edge declares how much the ring exceeds the 1px the recipes
|
|
33
|
+
* assume; the field recipes add it to --forest-input-depth, which keeps the recess reading the same
|
|
34
|
+
* at every size rather than flattening the ramp. `cut-fixed` selects the baked touch rungs.
|
|
35
|
+
*
|
|
36
|
+
* `[&.ring]` scopes the edge to the ringed variants, the same guard the badge cut carries below. A
|
|
37
|
+
* borderless field — `variant="none"`, which is what a command palette renders its search box as —
|
|
38
|
+
* has no `ring` class to thicken, so an unscoped edge painted a 2px box around it and fed
|
|
39
|
+
* --forest-field-edge into the depth-line shadow, bending the seam under the search row around the
|
|
40
|
+
* field's own corners. `cut-fixed` stays unscoped: the touch ladder applies to every field.
|
|
41
|
+
*/
|
|
42
|
+
const FIELD_EDGE = 'cut-fixed [&.ring]:ring-2 [&.ring]:[--forest-field-edge:1px]';
|
|
43
|
+
|
|
44
|
+
export const forestMobileTheme = {
|
|
45
|
+
button: {
|
|
46
|
+
// Focus is a POP, not a ring: a focused keycap/pad lifts past its rest lip (--btn-y-focus = -7px) and
|
|
47
|
+
// its ::before ring is switched off (--btn-ring-w = 0). All three are plain VARS the keycap recipe
|
|
48
|
+
// reads (PAD too) — NOT `before:`/`translate` utilities. That matters: the slot-level base hits every
|
|
49
|
+
// variant, and a `before:` utility here would auto-add `content:''` to the flat variants' ::before
|
|
50
|
+
// (which isn't absolute), turning it into a phantom leading flex item that `gap-1.5` shoves the label
|
|
51
|
+
// past. Vars touch only the variants that read them, so the flat variants stay put and gap-free.
|
|
52
|
+
// `cut-fixed` is the BAKED trigger for the shared touch ladder (styles/forest.css): the `touch:`
|
|
53
|
+
// rules in button.ts/input.ts fire either on a coarse pointer or on this marker, landing on the
|
|
54
|
+
// same rungs — 28/36/40/44/52 vs the fine-pointer 24/28/32/36/40. Baked, so the desktop mock and
|
|
55
|
+
// the native/Flutter export show true touch size with no pointer to query; and because both
|
|
56
|
+
// triggers name the same numbers, a real phone can't step twice. (Controls with no baked ladder of
|
|
57
|
+
// their own — badge, switch, checkbox, … — instead reach touch size via a bigger default `size`,
|
|
58
|
+
// so for THOSE this marker still means "opt out of the one-step `coarse:` bump".)
|
|
59
|
+
base: 'cut-fixed rounded-full [--btn-edge:2px] [--btn-y:-4px] [--btn-yi:4px] [--btn-y-h:-4px] [--btn-yhi:4px] [--btn-y-focus:-7px] [--btn-yi-focus:7px] [--btn-ring-w:0px] [--btn-hover:var(--btn-face)] dark:[--btn-hover:var(--btn-face)] [font-family:Mohr-BlackIt] italic font-black uppercase',
|
|
60
|
+
// Icon slots opt out of the coarse bump too — the preset hangs `coarse:size-*` on each icon slot
|
|
61
|
+
// (sm/lg), and the variant's `:not(.cut-fixed)` tests the element the utility sits on, so base
|
|
62
|
+
// alone would hold while the icon still grew a step. Same fix as the badge below.
|
|
63
|
+
leadingIcon: 'cut-fixed',
|
|
64
|
+
trailingIcon: 'cut-fixed',
|
|
65
|
+
},
|
|
66
|
+
// Card: one radius step up (2xl/32 over web's xl/24); --forest-card-radius follows so the nested
|
|
67
|
+
// concentric calc stays true (the 24→8 step becomes 32→16→8). Shape only — the mobile app keeps the
|
|
68
|
+
// readable normal title; the Mohr uppercase headline is marketing's voice, not the app's. Same
|
|
69
|
+
// radius as marketing (marketing adds the title on top).
|
|
70
|
+
card: {
|
|
71
|
+
// Roomier p-5 (20px) to carry the bigger title; --forest-card-pad follows so nested corners stay
|
|
72
|
+
// concentric (32→12→8). Radius one step up + the concentric var.
|
|
73
|
+
// The --fx-metric traits ride the ROOT, not a slot: a KPI figure sits in the card's body, so the
|
|
74
|
+
// var has to be declared above it. Mohr-BlackIt matches the title's cut.
|
|
75
|
+
root: 'rounded-2xl [--forest-card-radius:2rem] [--forest-card-pad:1.25rem] [--fx-metric-family:var(--font-display-italic)] [--fx-metric-transform:uppercase]',
|
|
76
|
+
body: 'sm:p-5',
|
|
77
|
+
header: 'sm:p-5',
|
|
78
|
+
footer: 'sm:p-5',
|
|
79
|
+
// Title: Mohr ITALIC (the Mohr-BlackIt cut, mobile's signature — the button uses the same face),
|
|
80
|
+
// uppercase (Mohr is ALWAYS uppercase), one size up. Family + transform via the --fx-title traits
|
|
81
|
+
// the preset's title slot reads; italic + weight as plain classes. Marketing uses the same face
|
|
82
|
+
// upright instead.
|
|
83
|
+
title: '[--fx-title-family:Mohr-BlackIt] [--fx-title-transform:uppercase] italic font-black text-xl',
|
|
84
|
+
// Description one size up for comfortable thumb reading; family stays GT Haptik (--font-sans).
|
|
85
|
+
description: 'text-base',
|
|
86
|
+
},
|
|
87
|
+
// Input: BAKES one size up (default md → lg, via forestMobileProps below) — the proportional touch
|
|
88
|
+
// step, same shape as button/badge. The native/Flutter surface is always touch but is prototyped on
|
|
89
|
+
// a fine-pointer desktop where a `coarse` media query never fires, so the step is baked via the size
|
|
90
|
+
// prop rather than the preset's adaptive `coarse:` promotion. `cut-fixed` opts this cut out of that
|
|
91
|
+
// promotion so a real coarse phone doesn't step AGAIN (lg→xl); it sits on base + the icon slots
|
|
92
|
+
// because those are the slots the preset bumps. Radius mirrors web — input.ts owns the ladder.
|
|
93
|
+
input: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
94
|
+
// Textarea: same touch step as input (a multiline input shares its identity). Bakes size lg via
|
|
95
|
+
// forestMobileProps + cut-fixed on the ladder slots, which selects the touch rungs.
|
|
96
|
+
textarea: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
97
|
+
// Field group: the overlap must equal the segment edge, or a joined seam paints both edges side by
|
|
98
|
+
// side. Stock's `-space-x-px` collapses two 1px rings to 1px; on this cut every segment carries a
|
|
99
|
+
// 2px edge, so the overlap steps to 2px (`-space-x-0.5`) to collapse them to one 2px line.
|
|
100
|
+
fieldGroup: { base: '-space-x-0.5' },
|
|
101
|
+
// Drawer: peach surface, matching mobile's default card variant — the same warm tint (light) /
|
|
102
|
+
// warm-950 (dark) fill and the aligned warm hairline ring, so a sheet and the cards inside it read
|
|
103
|
+
// as one family. Handle and edge share one solid mark colour (--forest-card-peach-mark: Bark Brown
|
|
104
|
+
// light, Acer Orange dark) instead of the stock neutral handle, which sat off-hue on peach. Radius stays with the preset (compoundVariants + the
|
|
105
|
+
// --forest-drawer-radius var re-pointed on the context marker class in styles/forest.css).
|
|
106
|
+
// TRAP: a local `:ui="{ content: '…' }"` on a UDrawer REPLACES this string (useComponentProps defus
|
|
107
|
+
// raw over theme, and defu overwrites strings rather than concatenating) — the surface silently
|
|
108
|
+
// reverts to bg-default. Pass one-off content classes via the `class` prop instead (it lands on
|
|
109
|
+
// content whenever the drawer has no trigger slot), which merges alongside the theme value.
|
|
110
|
+
drawer: {
|
|
111
|
+
// ring-2 over stock's 1px hairline, on --forest-card-peach-mark (Bark Brown light / Acer Orange
|
|
112
|
+
// dark) at /70 — matching what the handle actually paints, since vaul renders it at opacity .7
|
|
113
|
+
// at rest. A sheet floats over the page and needs an edge that holds its shape; the translucent
|
|
114
|
+
// peach CARD hairline is for something that sits IN the page.
|
|
115
|
+
content: 'bg-[var(--forest-card-peach-bg)] ring-2 ring-[var(--forest-card-peach-mark)]/70',
|
|
116
|
+
// Declared plain AND `dark:` — the PAD trap: the preset's handle sets `dark:bg-[var(--forest-700)]!`,
|
|
117
|
+
// which outranks a plain override in dark mode. The token is already mode-aware, so both point at it.
|
|
118
|
+
handle: 'bg-[var(--forest-card-peach-mark)]! dark:bg-[var(--forest-card-peach-mark)]!',
|
|
119
|
+
// Title in mobile's signature face — Mohr-BlackIt (the italic cut, licensed for this context
|
|
120
|
+
// only; web/marketing must not reach for it), ALWAYS uppercase, and stepped up from the preset's
|
|
121
|
+
// text-base to text-xl so a sheet header carries at arm's length. Same move as the mobile card
|
|
122
|
+
// title. Reaches the `title`-PROP path only; a free-form #header slot styles itself.
|
|
123
|
+
title: '[font-family:Mohr-BlackIt] italic font-black uppercase text-xl',
|
|
124
|
+
},
|
|
125
|
+
// Kbd: rounder corner (rounded-sm over web's rounded-xs); sm keeps rounded-xs (`[&.h-4]:`, stock
|
|
126
|
+
// sm height, (0,2,0) beats base) — at 16px tall rounded-sm reads as a pill. Padding is not touched
|
|
127
|
+
// here: the preset already runs px-2 (sm px-1), so this cut inherits it. Marketing matches.
|
|
128
|
+
kbd: { base: 'rounded-sm [&.h-4]:rounded-xs' },
|
|
129
|
+
// Alert: rounder banner (rounded-xl over web's rounded-lg) — the bold voice softens the corner a
|
|
130
|
+
// step further. Title weight comes from the preset, inherited. Marketing matches.
|
|
131
|
+
alert: { root: 'rounded-xl' },
|
|
132
|
+
// Calendar: rounder card frame (rounded-2xl over web's rounded-xl) — the bold voice softens the
|
|
133
|
+
// grid's containing card a step further, in step with the card cut. Shape only; the grid, date
|
|
134
|
+
// cells, heading weight and focus ring are all inherited from the preset.
|
|
135
|
+
calendar: { root: 'rounded-2xl' },
|
|
136
|
+
// Listbox: rounder shell (rounded-xl over the stock rounded-lg the standard cut leaves alone) —
|
|
137
|
+
// the bold voice takes the list from a field to a card. The corner has to be paid for: at 24px the
|
|
138
|
+
// arc cuts into the first and last rows, so the row inset steps 4px→12px and the rows sit inside
|
|
139
|
+
// the curve instead of under it.
|
|
140
|
+
//
|
|
141
|
+
// That inset lands on TWO slots because stock puts the padding in different places: a grouped list
|
|
142
|
+
// pads each `group` (and keeps the between-group rule full bleed on `content`), while a virtualised
|
|
143
|
+
// list renders rows straight into `content` and pads that instead. `[&.isolate]` scopes the content
|
|
144
|
+
// half to the virtualised cut — stock's own marker for it — so a grouped list is not double-padded.
|
|
145
|
+
// Coupled to that literal class; if it is ever renamed a virtualised list silently loses the inset.
|
|
146
|
+
// One flat value across xs→xl — a context delta cannot carry size variants. Marketing matches.
|
|
147
|
+
listbox: { root: 'rounded-xl', group: 'p-3', content: '[&.isolate]:p-3' },
|
|
148
|
+
// Badge: full pill (rounded-full over web's rounded-sm) — the bold voice rounds the chip all the
|
|
149
|
+
// way. Weight/case/tracking come from the preset, inherited here. The UTheme base merges after the
|
|
150
|
+
// preset's size variants, so a plain `rounded-full` wins the border-radius bucket at every size —
|
|
151
|
+
// no per-size override needed on this side. Marketing matches ON RADIUS.
|
|
152
|
+
//
|
|
153
|
+
// Touch increase, BAKED not adaptive: the default size steps up md→lg (forestMobileProps), and
|
|
154
|
+
// `cut-fixed` opts this cut out of the preset's `coarse:` bump (styles/forest.css) so it holds the
|
|
155
|
+
// baked size on a real coarse-pointer phone instead of promoting AGAIN. Same shape as the mobile
|
|
156
|
+
// button (props size + cut-fixed). Marketing does NOT do this — it has no cut-fixed, so it adapts
|
|
157
|
+
// via the preset coarse bump like standard.
|
|
158
|
+
// `cut-fixed` also on the icon slots, not just base: the preset hangs its `coarse:` bump on
|
|
159
|
+
// EACH slot (base carries text/padding/radius, leadingIcon/trailingIcon carry `coarse:size-*`),
|
|
160
|
+
// and the variant's `:not(.cut-fixed)` tests the element the utility sits on. Base alone would
|
|
161
|
+
// hold while the icon still promoted a step on a real coarse-pointer surface — so the opt-out has
|
|
162
|
+
// to land on every slot the preset bumps. Same shape needed on the button below.
|
|
163
|
+
// `[&.ring]` scopes the 2px edge to the ringed variants (outline/subtle) — a bare `ring-2` would
|
|
164
|
+
// paint a ring on solid/soft, which carry no ring classes at all. Coupled to stock's literal `ring`
|
|
165
|
+
// class from its colour×variant compounds; if that token is ever renamed the edge silently reverts.
|
|
166
|
+
badge: { base: 'rounded-full cut-fixed [&.ring]:ring-2', leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
167
|
+
// Tabs: bakes size lg (forestMobileProps) + cut-fixed on the slots the preset's coarse: ladder
|
|
168
|
+
// bumps (trigger + leadingIcon), so a real coarse phone holds lg instead of stepping to xl. Same
|
|
169
|
+
// shape as the input/badge mobile cut. Radius follows the size via the preset's pill ladder.
|
|
170
|
+
tabs: { trigger: 'cut-fixed', leadingIcon: 'cut-fixed' },
|
|
171
|
+
// Pin input: bakes size lg (forestMobileProps) + cut-fixed on the cell, which selects the touch rungs
|
|
172
|
+
// with no pointer to query. Same shape as the input mobile cut (a pin input is a split input).
|
|
173
|
+
pinInput: { base: FIELD_EDGE },
|
|
174
|
+
// Switch: bakes size lg + cut-fixed on every slot the preset's coarse: ladder bumps (track width,
|
|
175
|
+
// height, thumb, label) so a real phone holds lg instead of stepping to xl. The whole switch is the
|
|
176
|
+
// tap target, so the bigger size is a real touch win.
|
|
177
|
+
switch: { base: 'cut-fixed', container: 'cut-fixed', thumb: 'cut-fixed', wrapper: 'cut-fixed' },
|
|
178
|
+
// Checkbox / radio group: bake size lg + cut-fixed on the slots their coarse: ladders bump, so a
|
|
179
|
+
// real phone holds lg (taller tap rows) instead of stepping to xl.
|
|
180
|
+
checkbox: { base: 'cut-fixed', container: 'cut-fixed', wrapper: 'cut-fixed' },
|
|
181
|
+
radioGroup: { base: 'cut-fixed', item: 'cut-fixed', legend: 'cut-fixed', container: 'cut-fixed', fieldset: 'cut-fixed', indicator: 'cut-fixed' },
|
|
182
|
+
// Slider: bakes size lg + cut-fixed on the thumb — the only slot the preset's coarse: ladder bumps
|
|
183
|
+
// — so a real phone holds lg's bigger handle instead of stepping to xl.
|
|
184
|
+
slider: { thumb: 'cut-fixed' },
|
|
185
|
+
// Input date / time: field controls — same mobile cut as input. Bake size lg + cut-fixed on the
|
|
186
|
+
// ladder slots (base + the two icons), which selects the touch rungs on a fine-pointer mock.
|
|
187
|
+
inputDate: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
188
|
+
inputTime: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
189
|
+
// Input number: field control — same mobile cut as input. Bake size lg + cut-fixed on the base (its
|
|
190
|
+
// only ladder slot), which selects the touch rungs on a fine-pointer mock.
|
|
191
|
+
inputNumber: { base: FIELD_EDGE },
|
|
192
|
+
// Select: field control (trigger) — same mobile cut as input. Bake size lg + cut-fixed on the base
|
|
193
|
+
// and trigger icons (its ladder slots), which selects the touch rungs on a fine-pointer mock.
|
|
194
|
+
select: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
195
|
+
// Select menu / input menu: field controls (trigger + panel) — same mobile cut as input/select.
|
|
196
|
+
// Bake size lg + cut-fixed on base + trigger icons, selecting the touch rungs.
|
|
197
|
+
selectMenu: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
198
|
+
inputMenu: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
199
|
+
// Input tags: field control (chips + input) — same mobile cut. Bake size lg + cut-fixed on base +
|
|
200
|
+
// icons, selecting the touch rungs.
|
|
201
|
+
inputTags: { base: FIELD_EDGE, leadingIcon: 'cut-fixed', trailingIcon: 'cut-fixed' },
|
|
202
|
+
} as const;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Default PROPS for the mobile subtree — UTheme's `ui` only carries slot classes (a
|
|
206
|
+
* `defaultVariants` key in there is silently ignored), so per-component prop defaults ride the
|
|
207
|
+
* separate `props` channel: `<UTheme :ui="forestMobileTheme" :props="forestMobileProps">`.
|
|
208
|
+
* Mobile default emphasis: framed (web defaults to solid in the preset). Default size lg — which on
|
|
209
|
+
* the mobile cut renders at 44px (the `cut-fixed:` baked ladder in button.ts/input.ts: 28/36/40/44/52
|
|
210
|
+
* for xs→xl, vs web's 24/28/32/36/40), hitting Apple's 44pt min tap target; explicit `size` still wins
|
|
211
|
+
* and steps the same ladder (xl → 52). Web/marketing keep the shorter shared ladder.
|
|
212
|
+
*/
|
|
213
|
+
export const forestMobileProps = {
|
|
214
|
+
button: { variant: 'framed', size: 'lg' },
|
|
215
|
+
// Cards default to the peach accent on mobile — warm tint + aligned warm border, matching the
|
|
216
|
+
// drawer surface. Explicit variant props still win.
|
|
217
|
+
card: { variant: 'peach' },
|
|
218
|
+
// Badge default steps up md→lg on mobile — the baked touch increase (paired with `cut-fixed` in
|
|
219
|
+
// the theme so the preset's coarse: bump doesn't stack on top). Explicit size props still win.
|
|
220
|
+
badge: { size: 'lg' },
|
|
221
|
+
// Chip defaults to INSET on mobile. Stock overflows the host boundary, which is right for a square
|
|
222
|
+
// icon button but wrong here: the mobile button cut is a full pill and the app's avatars are round,
|
|
223
|
+
// so an overflowing dot floats off a curved edge with a visible gap. Inset holds it at the inner
|
|
224
|
+
// edge the host's own shape defines. No theme entry needed — stock owns the geometry (the overflow
|
|
225
|
+
// translate lives in compoundVariants keyed on `inset: false`, so simply not matching it IS the
|
|
226
|
+
// inset render), which makes this a prop default and nothing else. Explicit `:inset="false"` wins.
|
|
227
|
+
chip: { inset: true },
|
|
228
|
+
// Input default steps up md→lg — the baked touch step, same shape as button/badge. `cut-fixed` in the
|
|
229
|
+
// theme selects the touch rungs, so the desktop mock shows the same size a real phone does. Explicit
|
|
230
|
+
// size props still win.
|
|
231
|
+
input: { size: 'lg' },
|
|
232
|
+
// Field group steps up md→lg like every other field control. It matters MORE here than elsewhere:
|
|
233
|
+
// UFieldGroup PROVIDES its size to its children (`useFieldGroup`: a child resolves
|
|
234
|
+
// `props.size ?? fieldGroup.size`), so without a default here a mobile group fell back to md and
|
|
235
|
+
// dragged every joined child down to md with it — overriding their own lg mobile defaults. Setting
|
|
236
|
+
// it on the group is what makes a joined bar read at mobile size. An explicit `size` on the group
|
|
237
|
+
// (as the docs demos use) still wins.
|
|
238
|
+
fieldGroup: { size: 'lg' },
|
|
239
|
+
// Textarea mirrors input — baked one step up (md→lg) for comfortable thumb input.
|
|
240
|
+
textarea: { size: 'lg' },
|
|
241
|
+
// Tabs step up md→lg for touch-sized tap targets — the baked touch increase paired with cut-fixed
|
|
242
|
+
// in the theme so the preset's coarse: ladder doesn't step it again on a real phone.
|
|
243
|
+
tabs: { size: 'lg' },
|
|
244
|
+
// Pin input steps up md→lg for thumb-sized OTP cells — same baked touch increase as input.
|
|
245
|
+
pinInput: { size: 'lg' },
|
|
246
|
+
// Switch steps up md→lg — the whole control is the tap target, so bigger reads as easier to hit.
|
|
247
|
+
switch: { size: 'lg' },
|
|
248
|
+
// Checkbox / radio group step up md→lg — a taller tap row for touch.
|
|
249
|
+
checkbox: { size: 'lg' },
|
|
250
|
+
radioGroup: { size: 'lg' },
|
|
251
|
+
// Slider steps up md→lg — a bigger thumb to grab on touch.
|
|
252
|
+
slider: { size: 'lg' },
|
|
253
|
+
// Date / time fields step up md→lg — same touch step as input.
|
|
254
|
+
inputDate: { size: 'lg' },
|
|
255
|
+
inputTime: { size: 'lg' },
|
|
256
|
+
inputNumber: { size: 'lg' },
|
|
257
|
+
select: { size: 'lg' },
|
|
258
|
+
selectMenu: { size: 'lg' },
|
|
259
|
+
inputMenu: { size: 'lg' },
|
|
260
|
+
inputTags: { size: 'lg' },
|
|
261
|
+
} as const;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Manifest — what the mobile context touches (CONTEXTS-SPEC.md §4). `moves` is the hand-written
|
|
265
|
+
* record; `channels` is derived from the exports above and test-asserted.
|
|
266
|
+
*/
|
|
267
|
+
export const forestMobileManifest: ContextManifest = {
|
|
268
|
+
name: 'mobile',
|
|
269
|
+
binding: 'utheme',
|
|
270
|
+
surfaces: ['ride app (native/Flutter export; prototyped on desktop web)'],
|
|
271
|
+
darkPolicy: 'follows OS',
|
|
272
|
+
// The ride app is a touch surface — inherently coarse. Docs force the coarse simulation on and lock
|
|
273
|
+
// the pointer toggle here (see ContextScope / ContextSwitch); a "simulate touch" control on a
|
|
274
|
+
// context that IS touch is a no-op. Ladder components (button/input/…) carry cut-fixed and are already
|
|
275
|
+
// on the touch rungs; the rest promote under the forced coarse, exactly as on a real phone.
|
|
276
|
+
coarse: true,
|
|
277
|
+
components: {
|
|
278
|
+
button: {
|
|
279
|
+
moves:
|
|
280
|
+
'Full-pill keycap cut: 2px edge, deep anchored lip, Mohr-BlackIt italic uppercase label, focus pop instead of ring; framed/lg by default. Sits on the shared touch ladder — height + type 28/36/40/44/52 via the touch: rules in button.ts, selected here by the baked cut-fixed marker rather than by a pointer query, so the desktop mock matches a real phone.',
|
|
281
|
+
channels: ['props', 'slot', 'var'],
|
|
282
|
+
},
|
|
283
|
+
input: {
|
|
284
|
+
moves:
|
|
285
|
+
'lg default plus the shared touch ladder — height + type 28/36/40/44/52 via the touch: rules in input.ts (height-locked to the button so a joined field group matches), selected here by the baked cut-fixed marker rather than by a pointer query. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
286
|
+
channels: ['props', 'slot', 'var'],
|
|
287
|
+
},
|
|
288
|
+
textarea: {
|
|
289
|
+
moves:
|
|
290
|
+
'Mirrors input — bakes one size up (md→lg default) for thumb input, opted out of the adaptive coarse promotion. Radius mirrors web. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
291
|
+
channels: ['props', 'slot', 'var'],
|
|
292
|
+
},
|
|
293
|
+
drawer: {
|
|
294
|
+
moves:
|
|
295
|
+
'Peach surface matching the default card variant, with a solid Bark Brown mark (Acer Orange in dark) on both the 2px edge and the drag handle, and a Mohr-italic title one size up. Edge radius rounds to 2xl/32 and the container gets roomier 24px horizontal padding — those two ride CSS on the context marker class, not this theme, so the channel here is slots only.',
|
|
296
|
+
channels: ['slot'],
|
|
297
|
+
},
|
|
298
|
+
kbd: {
|
|
299
|
+
moves: 'Rounder corner (rounded-sm); the sm size keeps the tight rounded-xs. Same as marketing.',
|
|
300
|
+
channels: ['slot'],
|
|
301
|
+
},
|
|
302
|
+
listbox: {
|
|
303
|
+
moves:
|
|
304
|
+
'Rounder shell (rounded-xl over the stock corner the standard cut leaves alone), with the row inset stepped 4px→12px so the first and last rows clear the arc instead of tucking under it. The inset lands on both group and content because stock pads a grouped list and a virtualised one in different slots. One flat value across every size — a context delta carries no size variants. Same as marketing.',
|
|
305
|
+
channels: ['slot'],
|
|
306
|
+
},
|
|
307
|
+
badge: {
|
|
308
|
+
moves:
|
|
309
|
+
'Full-pill corner (rounded-full over the standard rounded-sm tag). Touch increase baked in: default size steps md→lg and cut-fixed opts out of the coarse bump so it holds that size. Marketing matches the corner but stays adaptive (no baked increase).',
|
|
310
|
+
channels: ['props', 'slot'],
|
|
311
|
+
},
|
|
312
|
+
chip: {
|
|
313
|
+
moves:
|
|
314
|
+
'Defaults to inset — the dot sits inside the host boundary instead of overflowing it, because the mobile button is a full pill and the app\'s avatars are round, so an overflowing dot floats off a curved edge. Geometry is untouched stock (the overflow translate is a compound variant keyed on inset: false); this is a prop default and nothing more.',
|
|
315
|
+
channels: ['props'],
|
|
316
|
+
},
|
|
317
|
+
alert: {
|
|
318
|
+
moves: 'Rounder banner corner (rounded-xl over the standard rounded-lg). Same as marketing.',
|
|
319
|
+
channels: ['slot'],
|
|
320
|
+
},
|
|
321
|
+
calendar: {
|
|
322
|
+
moves:
|
|
323
|
+
'Rounder card frame (rounded-2xl over the standard rounded-xl) — shape only; grid, date cells, heading and focus ring are inherited.',
|
|
324
|
+
channels: ['slot'],
|
|
325
|
+
},
|
|
326
|
+
card: {
|
|
327
|
+
moves:
|
|
328
|
+
'Rounder cut: one radius step up (rounded-2xl/32 over the standard xl/24); --forest-card-radius follows so nested cards step 32→12→8 at the roomier p-5. Title is Mohr italic + uppercase (the Mohr-BlackIt cut), one size up; description one size up, still GT Haptik. Defaults to the peach accent variant. A KPI figure inside the card takes the same face through the --fx-metric traits. Same radius as marketing; marketing title is the same face upright.',
|
|
329
|
+
channels: ['props', 'slot', 'var'],
|
|
330
|
+
},
|
|
331
|
+
tabs: {
|
|
332
|
+
moves:
|
|
333
|
+
'Bakes size lg default (md→lg) for touch tap targets; cut-fixed opts the trigger/icon out of the preset coarse ladder so it holds instead of stepping to xl.',
|
|
334
|
+
channels: ['props', 'slot'],
|
|
335
|
+
},
|
|
336
|
+
pinInput: {
|
|
337
|
+
moves:
|
|
338
|
+
'Bakes size lg default (md→lg) for thumb-sized OTP cells; cut-fixed puts the cell on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
339
|
+
channels: ['props', 'slot', 'var'],
|
|
340
|
+
},
|
|
341
|
+
switch: {
|
|
342
|
+
moves:
|
|
343
|
+
'Bakes size lg default (md→lg) for a thumb-sized tap target; cut-fixed opts the track/thumb/label out of the preset coarse ladder so it holds instead of stepping to xl.',
|
|
344
|
+
channels: ['props', 'slot'],
|
|
345
|
+
},
|
|
346
|
+
checkbox: {
|
|
347
|
+
moves:
|
|
348
|
+
'Bakes size lg default (md→lg) for a taller tap row; cut-fixed opts the box/row/label out of the preset coarse ladder so it holds instead of stepping to xl.',
|
|
349
|
+
channels: ['props', 'slot'],
|
|
350
|
+
},
|
|
351
|
+
radioGroup: {
|
|
352
|
+
moves:
|
|
353
|
+
'Bakes size lg default (md→lg) for a taller tap row; cut-fixed opts the circle/row/text out of the preset coarse ladder so it holds instead of stepping to xl.',
|
|
354
|
+
channels: ['props', 'slot'],
|
|
355
|
+
},
|
|
356
|
+
slider: {
|
|
357
|
+
moves:
|
|
358
|
+
'Bakes size lg default (md→lg) for a bigger grab thumb; cut-fixed opts the thumb out of the preset coarse ladder so it holds instead of stepping to xl.',
|
|
359
|
+
channels: ['props', 'slot'],
|
|
360
|
+
},
|
|
361
|
+
inputDate: {
|
|
362
|
+
moves:
|
|
363
|
+
'Bakes size lg default (md→lg) — the same touch step as input; cut-fixed puts the field/icons on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
364
|
+
channels: ['props', 'slot', 'var'],
|
|
365
|
+
},
|
|
366
|
+
inputTime: {
|
|
367
|
+
moves:
|
|
368
|
+
'Bakes size lg default (md→lg) — the same touch step as input; cut-fixed puts the field/icons on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
369
|
+
channels: ['props', 'slot', 'var'],
|
|
370
|
+
},
|
|
371
|
+
inputNumber: {
|
|
372
|
+
moves:
|
|
373
|
+
'Bakes size lg default (md→lg) — the same touch step as input; cut-fixed puts the field on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
374
|
+
channels: ['props', 'slot', 'var'],
|
|
375
|
+
},
|
|
376
|
+
select: {
|
|
377
|
+
moves:
|
|
378
|
+
'Bakes size lg default (md→lg) — the same touch step as input; cut-fixed puts the trigger/icons on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
379
|
+
channels: ['props', 'slot', 'var'],
|
|
380
|
+
},
|
|
381
|
+
selectMenu: {
|
|
382
|
+
moves:
|
|
383
|
+
'Bakes size lg default (md→lg) — the same touch step as input; cut-fixed puts the trigger/icons on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
384
|
+
channels: ['props', 'slot', 'var'],
|
|
385
|
+
},
|
|
386
|
+
inputMenu: {
|
|
387
|
+
moves:
|
|
388
|
+
'Bakes size lg default (md→lg) — the same touch step as input; cut-fixed puts the field/icons on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
389
|
+
channels: ['props', 'slot', 'var'],
|
|
390
|
+
},
|
|
391
|
+
inputTags: {
|
|
392
|
+
moves:
|
|
393
|
+
'Bakes size lg default (md→lg) — the same touch step as input; cut-fixed puts the field/icons on the shared touch ladder (28/36/40/44/52) with no pointer to query — the same rungs a coarse pointer selects. Carries the shared 2px field edge (matching the button) with the depth line stepped to 5px so the recess still reads 3px under it.',
|
|
394
|
+
channels: ['props', 'slot', 'var'],
|
|
395
|
+
},
|
|
396
|
+
fieldGroup: {
|
|
397
|
+
moves:
|
|
398
|
+
'Bakes size lg default (md→lg). UFieldGroup provides its size to joined children, so setting it here makes a mobile group read at lg instead of dragging its children back down to md. Steps the overlap to 2px (-space-x-0.5) to match this cut\'s 2px segment edge, so a joined seam collapses to one line instead of painting both edges.',
|
|
399
|
+
channels: ['props', 'slot'],
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The filter row: a trigger, applied chips, and clear-all — the whole thing in one bar.
|
|
3
|
+
//
|
|
4
|
+
// This is the composition, not the implementation. <FFilterTrigger> is the add-filter control and
|
|
5
|
+
// <FFilterClauses> is the row of chips; both are controlled by the same Clause[], so a page that
|
|
6
|
+
// wants them in DIFFERENT bars writes the two directly and binds them to one v-model:
|
|
7
|
+
//
|
|
8
|
+
// <FPageToolbar>
|
|
9
|
+
// <template #left><UNavigationMenu :items="views" /></template>
|
|
10
|
+
// <template #right><FFilterTrigger v-model="clauses" :schema="schema" /></template>
|
|
11
|
+
// </FPageToolbar>
|
|
12
|
+
//
|
|
13
|
+
// <FPageToolbar v-if="clauses.length">
|
|
14
|
+
// <FFilterClauses v-model="clauses" :schema="schema" />
|
|
15
|
+
// </FPageToolbar>
|
|
16
|
+
//
|
|
17
|
+
// Nothing is teleported or injected to make that work — sharing a v-model is the whole mechanism.
|
|
18
|
+
//
|
|
19
|
+
// Controlled. The bar's data contract is a Clause[]: it never receives rows and never filters. A
|
|
20
|
+
// consumer passes the clauses to applyClauses, or toColumnFilters' output to a table.
|
|
21
|
+
import { computed, useTemplateRef } from 'vue';
|
|
22
|
+
import FFilterClauses from './FFilterClauses.vue';
|
|
23
|
+
import FFilterTrigger from './FFilterTrigger.vue';
|
|
24
|
+
import type { FilterSlot } from './filter.theme';
|
|
25
|
+
import type { FilterSchema } from './filterSchema';
|
|
26
|
+
import type { Clause } from './filterClause';
|
|
27
|
+
|
|
28
|
+
const props = defineProps<{
|
|
29
|
+
modelValue: Clause[];
|
|
30
|
+
schema: FilterSchema;
|
|
31
|
+
/** Text beside the trigger's icon. Off by default — see <FFilterTrigger>. */
|
|
32
|
+
label?: string;
|
|
33
|
+
ui?: Partial<Record<FilterSlot, string>>;
|
|
34
|
+
}>();
|
|
35
|
+
|
|
36
|
+
const emit = defineEmits<{ 'update:modelValue': [clauses: Clause[]] }>();
|
|
37
|
+
|
|
38
|
+
const trigger = useTemplateRef<InstanceType<typeof FFilterTrigger>>('trigger');
|
|
39
|
+
const clauses = useTemplateRef<InstanceType<typeof FFilterClauses>>('clauses');
|
|
40
|
+
|
|
41
|
+
const update = (next: Clause[]) => emit('update:modelValue', next);
|
|
42
|
+
|
|
43
|
+
const model = computed(() => props.modelValue);
|
|
44
|
+
|
|
45
|
+
defineExpose({ open: () => trigger.value?.open() });
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<!-- The trigger goes through the clause row's slot rather than beside it, so both halves share ONE
|
|
50
|
+
flex flow: the trigger holds its place at the head of the row and the chips wrap past it,
|
|
51
|
+
which is what they did when this was a single component. -->
|
|
52
|
+
<FFilterClauses
|
|
53
|
+
ref="clauses"
|
|
54
|
+
:model-value="model"
|
|
55
|
+
:schema="props.schema"
|
|
56
|
+
:trigger="trigger"
|
|
57
|
+
:ui="props.ui"
|
|
58
|
+
@update:model-value="update"
|
|
59
|
+
>
|
|
60
|
+
<template #trigger>
|
|
61
|
+
<FFilterTrigger
|
|
62
|
+
ref="trigger"
|
|
63
|
+
:model-value="model"
|
|
64
|
+
:schema="props.schema"
|
|
65
|
+
:clauses="clauses"
|
|
66
|
+
:label="props.label"
|
|
67
|
+
:ui="props.ui"
|
|
68
|
+
@update:model-value="update"
|
|
69
|
+
/>
|
|
70
|
+
</template>
|
|
71
|
+
</FFilterClauses>
|
|
72
|
+
</template>
|