@mohasinac/appkit 3.3.6 → 3.4.1

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 (141) hide show
  1. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +2 -1
  2. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +2 -1
  3. package/dist/_internal/client/features/live/LiveItemDetailView.js +2 -1
  4. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +1 -1
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.js +2 -1
  6. package/dist/_internal/client/features/lottery/LotteryListView.js +2 -1
  7. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +2 -1
  8. package/dist/_internal/server/features/checkout/actions.js +0 -1
  9. package/dist/_internal/server/features/lottery/actions.js +0 -2
  10. package/dist/_internal/server/features/reviews/actions.js +1 -1
  11. package/dist/features/about/components/FAQPageView.js +0 -2
  12. package/dist/features/about/components/FeesView.js +13 -1
  13. package/dist/features/about/components/HowCheckoutWorksView.js +17 -3
  14. package/dist/features/about/components/HowEmiWorksView.d.ts +3 -0
  15. package/dist/features/about/components/HowEmiWorksView.js +117 -0
  16. package/dist/features/about/components/PublicProfileView.js +1 -4
  17. package/dist/features/about/index.d.ts +2 -0
  18. package/dist/features/about/index.js +1 -0
  19. package/dist/features/account/components/BecomeSellerView.d.ts +1 -1
  20. package/dist/features/account/components/BecomeSellerView.js +1 -1
  21. package/dist/features/account/components/NotificationBell.js +1 -2
  22. package/dist/features/account/components/UserSupportView.js +0 -1
  23. package/dist/features/admin/components/AdminAdsView.js +3 -3
  24. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  25. package/dist/features/admin/components/AdminBundleEditorView.js +0 -4
  26. package/dist/features/admin/components/AdminCarouselEditorView.js +3 -3
  27. package/dist/features/admin/components/AdminCarouselView.js +3 -2
  28. package/dist/features/admin/components/AdminGuideHubView.js +2 -2
  29. package/dist/features/admin/components/AdminMediaView.js +2 -4
  30. package/dist/features/admin/components/AdminUserEditorView.d.ts +1 -1
  31. package/dist/features/admin/components/AdminUserEditorView.js +1 -1
  32. package/dist/features/admin/components/QuickActionsPanel.js +2 -2
  33. package/dist/features/auctions/components/AuctionDetailPageView.js +1 -1
  34. package/dist/features/blog/components/BlogFeaturedCard.js +2 -1
  35. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  36. package/dist/features/blog/components/BlogListView.js +2 -1
  37. package/dist/features/blog/components/BlogPostForm.js +1 -1
  38. package/dist/features/blog/components/BlogPostView.js +2 -1
  39. package/dist/features/cart/components/CartDrawer.js +2 -1
  40. package/dist/features/categories/components/BrandDetailPageView.js +2 -1
  41. package/dist/features/categories/components/BundleCollage.js +2 -3
  42. package/dist/features/categories/components/CategoriesIndexListing.js +1 -5
  43. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  44. package/dist/features/categories/components/CategoryGrid.js +2 -1
  45. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -1
  46. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  47. package/dist/features/categories/components/ConcernCard.js +2 -1
  48. package/dist/features/collections/components/CollectionCard.js +2 -1
  49. package/dist/features/consultation/components/ConsultationForm.js +0 -1
  50. package/dist/features/contact/components/ContactInfoSidebar.js +2 -2
  51. package/dist/features/copilot/components/AdminCopilotView.js +0 -9
  52. package/dist/features/events/components/EventFormDrawer.d.ts +1 -1
  53. package/dist/features/events/components/EventFormDrawer.js +1 -1
  54. package/dist/features/events/components/EventPollWidget.js +2 -2
  55. package/dist/features/events/components/EventsIndexListing.js +1 -1
  56. package/dist/features/faq/actions/faq-actions.d.ts +2 -10
  57. package/dist/features/faq/actions/faq-actions.js +0 -4
  58. package/dist/features/homepage/components/AdSlot.js +2 -1
  59. package/dist/features/homepage/components/CustomCardsSection.js +2 -3
  60. package/dist/features/homepage/components/FeaturedBundlesSection.js +2 -3
  61. package/dist/features/homepage/components/GoogleReviewsSection.js +4 -3
  62. package/dist/features/homepage/components/HeroBanner.js +0 -1
  63. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -1
  64. package/dist/features/homepage/components/SectionCarousel.js +1 -2
  65. package/dist/features/homepage/components/SocialFeedSection.js +2 -5
  66. package/dist/features/homepage/components/SocialPostCard.js +4 -3
  67. package/dist/features/media/MediaImage.d.ts +10 -1
  68. package/dist/features/media/MediaImage.js +6 -2
  69. package/dist/features/media/MediaPickerModal.js +2 -3
  70. package/dist/features/media/upload/MediaUploadList.js +2 -2
  71. package/dist/features/orders/components/OrdersList.js +2 -1
  72. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -0
  73. package/dist/features/products/api/route.js +1 -2
  74. package/dist/features/products/components/CompareOverlay.js +2 -3
  75. package/dist/features/products/components/CustomSectionTabContent.js +2 -2
  76. package/dist/features/products/components/FeatureBadge.js +0 -6
  77. package/dist/features/products/components/GroupSettingsPanel.js +2 -3
  78. package/dist/features/products/components/MarketplaceBundleCard.js +2 -1
  79. package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -3
  80. package/dist/features/products/components/PrizeDrawCollage.js +2 -3
  81. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -1
  82. package/dist/features/products/components/PrizeDrawEntryActions.js +3 -3
  83. package/dist/features/products/components/PrizeRevealModal.js +3 -4
  84. package/dist/features/products/components/ProductDetailActions.d.ts +1 -1
  85. package/dist/features/products/components/ProductDetailActions.js +1 -1
  86. package/dist/features/products/components/ProductDetailPageView.d.ts +3 -1
  87. package/dist/features/products/components/ProductDetailPageView.js +3 -2
  88. package/dist/features/products/components/ProductFeatureBadges.d.ts +7 -1
  89. package/dist/features/products/components/ProductFeatureBadges.js +16 -3
  90. package/dist/features/products/components/ProductGradingTab.js +2 -2
  91. package/dist/features/products/components/ProductsIndexPageView.js +2 -2
  92. package/dist/features/products/components/SublistingCarouselSection.js +3 -4
  93. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -1
  94. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  95. package/dist/features/reviews/components/ReviewDetailShell.js +4 -3
  96. package/dist/features/reviews/components/ReviewModal.js +2 -1
  97. package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
  98. package/dist/features/reviews/components/ReviewsList.js +2 -1
  99. package/dist/features/scams/components/ScamAwarenessModal.js +2 -2
  100. package/dist/features/search/components/SearchView.d.ts +1 -1
  101. package/dist/features/search/components/SearchView.js +1 -1
  102. package/dist/features/seller/components/SellerAddressesView.js +1 -5
  103. package/dist/features/seller/components/SellerPreOrdersView.js +3 -2
  104. package/dist/features/seller/components/SellerPrizeDrawsView.js +3 -2
  105. package/dist/features/seller/components/SellerProductsView.js +2 -1
  106. package/dist/features/seller/components/SellerSidebar.js +5 -4
  107. package/dist/features/shell/StepForm.js +1 -1
  108. package/dist/features/stores/components/InteractiveStoreCard.js +2 -3
  109. package/dist/features/stores/components/StoreAboutView.js +2 -2
  110. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +2 -2
  111. package/dist/features/stores/components/StoreFinanceGuideView.js +2 -2
  112. package/dist/features/stores/components/StoreGuideHubView.js +2 -2
  113. package/dist/features/stores/components/StoreHeader.js +2 -1
  114. package/dist/features/stores/components/StoreListingsGuideView.js +2 -2
  115. package/dist/features/stores/components/StoreNavTabs.js +2 -2
  116. package/dist/features/stores/components/StoreOrdersGuideView.js +2 -2
  117. package/dist/features/stores/components/StoreSettingsGuideView.js +3 -3
  118. package/dist/features/wishlist/components/WishlistPage.js +2 -1
  119. package/dist/features/wishlist/components/WishlistView.js +0 -7
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +3 -0
  122. package/dist/next/routing/route-map.d.ts +2 -0
  123. package/dist/next/routing/route-map.js +1 -0
  124. package/dist/seed/factories/product.factory.js +0 -32
  125. package/dist/seed/faq-seed-data.js +23 -4
  126. package/dist/styles.css +4 -1
  127. package/dist/tailwind-utilities.css +1 -1
  128. package/dist/tokens/index.js +6 -458
  129. package/dist/ui/DataTable.js +0 -21
  130. package/dist/ui/components/Anchor.d.ts +3 -1
  131. package/dist/ui/components/Anchor.js +1 -0
  132. package/dist/ui/components/Avatar.style.css +3 -0
  133. package/dist/ui/components/Breadcrumb.js +2 -1
  134. package/dist/ui/components/FormActionBar.js +2 -1
  135. package/dist/ui/components/ListingLayout.d.ts +1 -1
  136. package/dist/ui/components/ListingLayout.js +1 -1
  137. package/dist/ui/components/Motion.js +1 -1
  138. package/dist/ui/components/QuickCreateModal.js +2 -1
  139. package/dist/ui/components/SlottedListingView.d.ts +1 -1
  140. package/dist/ui/components/SlottedListingView.js +1 -2
  141. package/package.json +1 -1
@@ -189,41 +189,6 @@ export const LOCALE_CONFIG = {
189
189
  // Projects (letitrip.in, licorice, hobson) import this as their base and
190
190
  // extend with brand-specific values. The `primary`/`secondary` Tailwind color
191
191
  // names are resolved by each consumer project's tailwind.config.js.
192
- /**
193
- * Dark-mode aware classes for backgrounds, text, and borders.
194
- * Base = zinc (light) / slate (dark — navy-tinted).
195
- */
196
- const THEMED = {
197
- // Backgrounds
198
- bgPrimary: "bg-zinc-50 bg-[var(--appkit-color-bg)]",
199
- bgSecondary: "bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface)]",
200
- bgTertiary: "bg-zinc-200 bg-[var(--appkit-color-surface-elevated)]",
201
- bgElevated: "bg-[var(--appkit-color-surface)]",
202
- bgInput: "bg-[var(--appkit-color-surface-input)]",
203
- // Text
204
- textPrimary: "text-[var(--appkit-color-text)]",
205
- textSecondary: "text-[var(--appkit-color-text-muted)]",
206
- textMuted: "text-[var(--appkit-color-text-faint)]",
207
- textError: "text-red-600 dark:text-red-400",
208
- textSuccess: "text-emerald-600 dark:text-emerald-400",
209
- textOnPrimary: "text-white",
210
- textOnDark: "text-white",
211
- // Borders
212
- border: "border-[var(--appkit-color-border)]",
213
- borderSubtle: "border-[var(--appkit-color-border-subtle)]",
214
- borderLight: "border-zinc-100 border-[var(--appkit-color-border)]/60",
215
- borderError: "border-red-500",
216
- borderColor: "border-[var(--appkit-color-border)]",
217
- // Interactive
218
- hover: "hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)]",
219
- hoverCard: "hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]/60",
220
- hoverBorder: "hover:border-[var(--appkit-color-border)]",
221
- hoverText: "hover:text-zinc-800 dark:hover:text-zinc-100",
222
- activeRow: "bg-primary-50 dark:bg-secondary-950/30",
223
- focusRing: "focus:ring-primary-500 dark:focus:ring-secondary-400",
224
- divider: "divide-zinc-200 divide-[var(--appkit-color-border)]",
225
- placeholder: "placeholder-zinc-400 dark:placeholder-zinc-500",
226
- };
227
192
  /**
228
193
  * Layout dimension constants (height, width strings for Tailwind).
229
194
  */
@@ -287,138 +252,6 @@ export const LAYOUT = {
287
252
  full: "w-full",
288
253
  },
289
254
  };
290
- /**
291
- * Typography scale — fluid steps across sm/md/lg breakpoints.
292
- */
293
- const TYPOGRAPHY = {
294
- pageTitle: "text-2xl md:text-3xl lg:text-4xl font-bold tracking-tight leading-tight",
295
- pageSubtitle: "text-base md:text-lg text-[var(--appkit-color-text-muted)] leading-relaxed mt-1",
296
- sectionTitle: "text-xl md:text-2xl lg:text-3xl font-semibold tracking-tight",
297
- sectionSubtitle: "text-sm md:text-base lg:text-lg text-[var(--appkit-color-text-muted)]",
298
- cardTitle: "text-base md:text-lg font-semibold leading-snug",
299
- cardBody: "text-sm md:text-base leading-relaxed",
300
- label: "text-sm font-medium text-[var(--appkit-color-text-muted)]",
301
- caption: "text-xs text-[var(--appkit-color-text-muted)]",
302
- overline: "text-xs font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)]",
303
- h1: "text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight font-display",
304
- h2: "text-2xl md:text-3xl lg:text-4xl font-bold tracking-tight font-display",
305
- h3: "text-xl md:text-2xl lg:text-3xl font-bold tracking-tight font-display",
306
- h4: "text-lg md:text-xl lg:text-2xl font-bold font-display",
307
- h5: "text-base md:text-lg lg:text-xl font-medium",
308
- h6: "text-sm md:text-base lg:text-lg font-medium",
309
- body: "text-base lg:text-lg",
310
- small: "text-sm lg:text-base",
311
- xs: "text-xs lg:text-sm",
312
- display: "text-8xl md:text-9xl font-bold font-display",
313
- };
314
- /**
315
- * Spacing — section, form, and element stacking patterns + gap token map.
316
- * The gap map mirrors the GAP_MAP inlined in @mohasinac/ui Layout.tsx.
317
- */
318
- const SPACING = {
319
- section: "space-y-8 md:space-y-12 lg:space-y-16",
320
- formGroup: "space-y-6 lg:space-y-8",
321
- stack: "space-y-4",
322
- stackSmall: "space-y-2",
323
- inline: "space-x-4",
324
- inlineSmall: "gap-2 lg:gap-3",
325
- inlineLarge: "gap-4 lg:gap-6",
326
- pageY: "py-6 sm:py-8 lg:py-10",
327
- sectionGap: "mt-8 md:mt-12",
328
- cardPadding: "p-5 sm:p-6 lg:p-8",
329
- padding: {
330
- xs: "p-2",
331
- sm: "p-3",
332
- md: "p-4",
333
- lg: "p-6",
334
- xl: "p-8",
335
- },
336
- gap: {
337
- none: "",
338
- px: "gap-px",
339
- xs: "gap-1",
340
- sm: "gap-2",
341
- "2.5": "gap-2.5",
342
- "3": "gap-3",
343
- md: "gap-4",
344
- lg: "gap-6",
345
- xl: "gap-8",
346
- "2xl": "gap-12",
347
- },
348
- };
349
- /**
350
- * Responsive grid presets — mobile-first, every preset reaches xl: and 2xl:.
351
- *
352
- * Rules:
353
- * - Fixed column grids (cols1–cols6) scale gracefully to widescreen.
354
- * - Card grids start at 1 col on portrait mobile (≤639 px) for readability.
355
- * - Auto-fill grids use minmax so columns form naturally at any viewport width.
356
- */
357
- const GRID = {
358
- /** Single column */
359
- cols1: "grid grid-cols-1",
360
- /** 1 → 2 */
361
- cols2: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2",
362
- /** 1 → 2 (md breakpoint) */
363
- cols2Md: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2",
364
- /** 1 → 2 → 3 → 4 on widescreen */
365
- cols3: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4",
366
- /** 1 → 2 → 3 → 4 → 5 on widescreen */
367
- cols4: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5",
368
- /** 1 → 2 → 3 → 4 → 5 */
369
- cols5: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5",
370
- /** 2 → 3 → 4 → 5 → 6 */
371
- cols6: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6",
372
- /** 2 → 3 → 6 (lg breakpoint) */
373
- cols6Lg: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6",
374
- /**
375
- * Generic card grid — 1 col on portrait mobile → 5 on ultrawide.
376
- * Starts at 1 (not 2) so product cards with title+image+price are readable
377
- * even on 320 px handsets in portrait orientation.
378
- */
379
- cards: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5",
380
- /** Auto-fill product cards — min 200 px */
381
- productCards: "grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-6",
382
- /** Auto-fill store cards — min 220 px */
383
- storeCards: "grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-6",
384
- /** Auto-fill category icon+label tiles — min 130 px */
385
- categoryCards: "grid grid-cols-[repeat(auto-fill,minmax(130px,1fr))] gap-4",
386
- /** Auto-fill coupon/promo cards — min 264 px */
387
- couponCards: "grid grid-cols-[repeat(auto-fill,minmax(264px,1fr))] gap-6",
388
- /** Auto-fill address / wide cards — min 300 px */
389
- addressCards: "grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] gap-4",
390
- /** Auto-fill KPI/stat tiles — min 180 px */
391
- statTiles: "grid grid-cols-[repeat(auto-fill,minmax(180px,1fr))] gap-4",
392
- /** Auto-fill account nav tiles — min 160 px */
393
- navTiles: "grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-4",
394
- /** Auto-fill, min 200 px */
395
- autoFillSm: "grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))]",
396
- /** Auto-fill, min 280 px */
397
- autoFillMd: "grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))]",
398
- /** Auto-fill, min 360 px */
399
- autoFillLg: "grid grid-cols-[repeat(auto-fill,minmax(360px,1fr))]",
400
- /** Fixed 280 px left sidebar + 1fr */
401
- sidebar: "grid grid-cols-1 lg:grid-cols-[280px_1fr]",
402
- /** 1fr + fixed 280 px right sidebar */
403
- sidebarRight: "grid grid-cols-1 lg:grid-cols-[1fr_280px]",
404
- /** Fixed 320 px left sidebar + 1fr (admin) */
405
- sidebarWide: "grid grid-cols-1 lg:grid-cols-[320px_1fr]",
406
- /** Equal halves on md+ */
407
- halves: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2",
408
- /** 2fr / 1fr */
409
- twoThird: "grid grid-cols-1 md:grid-cols-[2fr_1fr]",
410
- /** 1fr / 2fr */
411
- oneThird: "grid grid-cols-1 md:grid-cols-[1fr_2fr]",
412
- // Phase 20 mobile-first named grid presets
413
- /** Product grid — 2 cols on mobile, scales to 5 on ultrawide */
414
- products: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 sm:gap-4 xl:gap-6",
415
- /** Wide card grid — 1 col on mobile → 3 on xl */
416
- wide: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 xl:gap-8",
417
- /** Admin stats grid — 1 col → 4 KPI tiles on xl */
418
- admin: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4",
419
- /** Filter sidebar + main content + optional right aside */
420
- withFilter: "grid grid-cols-1 lg:grid-cols-[220px_1fr] xl:grid-cols-[260px_1fr_200px] gap-4 xl:gap-6",
421
- };
422
255
  // --- FLUID GRID -------------------------------------------------------------
423
256
  /**
424
257
  * Minimum item widths for auto-fill fluid grids matching the four
@@ -442,298 +275,13 @@ export const FLUID_GRID_MIN_WIDTHS = {
442
275
  /** Filter chips */
443
276
  chip: 80,
444
277
  };
445
- /**
446
- * Tailwind CSS class strings for fluid auto-fill grids.
447
- * Use these instead of `grid grid-cols-*` breakpoint classes.
448
- */
449
- const FLUID_GRID = {
450
- /** Product / store cards — min 220 px */
451
- card: "fluid-grid-card",
452
- /** Admin stat / user cards — min 260 px */
453
- admin: "fluid-grid-admin",
454
- /** Blog / event cards — min 300 px */
455
- wide: "fluid-grid-wide",
456
- /** Gallery thumbnails — min 160 px */
457
- thumb: "fluid-grid-thumb",
458
- };
459
- /**
460
- * Page container presets — max-width + centering + responsive horizontal padding.
461
- * Mirrors the CONTAINER_MAP inlined in @mohasinac/ui Layout.tsx.
462
- */
463
- const PAGE = {
464
- px: "px-4 sm:px-6 lg:px-8",
465
- container: {
466
- /** max-w-3xl — blog / policy */
467
- sm: "max-w-3xl mx-auto px-4 sm:px-6 lg:px-8",
468
- /** max-w-4xl — contact / about */
469
- md: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8",
470
- /** max-w-5xl — checkout / help */
471
- lg: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8",
472
- /** max-w-6xl — product detail / cart */
473
- xl: "max-w-6xl mx-auto px-4 sm:px-6 lg:px-8",
474
- /** max-w-7xl — main content grids (default) */
475
- "2xl": "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",
476
- /** max-w-screen-2xl — full-bleed content */
477
- full: "max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-8",
478
- /** max-w-screen-2xl — wide store/seller layouts (compact px) */
479
- wide: "max-w-screen-2xl mx-auto px-4 sm:px-6",
480
- /** max-w-[1920px] — ultra-wide / 4K displays */
481
- ultra: "max-w-[1920px] mx-auto px-4 sm:px-6 lg:px-8",
482
- },
483
- };
484
- /**
485
- * Form input styles — border, bg, focus ring, error/success/disabled states.
486
- */
487
- const INPUT = {
488
- base: "rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-3.5 py-2.5 text-base sm:text-sm text-[var(--appkit-color-text)] focus:ring-2 focus:ring-primary-500/40 focus:border-primary-500 focus:outline-none transition-colors duration-150 placeholder:text-[var(--appkit-color-text-faint)]",
489
- error: "border-red-400 dark:border-red-500 focus:ring-red-500/20 focus:border-red-500 bg-red-50/30 dark:bg-red-950/10",
490
- success: "border-emerald-400 dark:border-emerald-500 focus:ring-emerald-500/20 focus:border-emerald-500",
491
- disabled: "bg-zinc-50 bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text-faint)] cursor-not-allowed opacity-60",
492
- withIcon: "pl-10",
493
- };
494
- /**
495
- * Card styles — base, hover, elevated, glass, and interactive variants.
496
- */
497
- const CARD = {
498
- base: "rounded-2xl overflow-hidden transition-all",
499
- shadow: "shadow-sm",
500
- shadowElevated: "shadow-lg",
501
- hover: "hover:shadow-md dark:hover:shadow-xl cursor-pointer transition-shadow duration-200",
502
- standard: "rounded-2xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] shadow-sm hover:shadow-md transition-all duration-200",
503
- elevated: "rounded-2xl bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md hover:shadow-lg transition-shadow duration-200",
504
- interactive: "rounded-2xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] shadow-sm hover:shadow-md hover:border-primary-300/60 dark:hover:border-secondary-500/60 transition-all duration-200 cursor-pointer",
505
- glass: "rounded-2xl backdrop-blur-md bg-[color-mix(in_srgb,var(--appkit-color-surface)_85%,transparent)] border border-[var(--appkit-color-border-subtle)] shadow-lg",
506
- // Card anatomy tokens (Phase 20)
507
- /** Product card image wrapper — portrait aspect ratio */
508
- image: "relative aspect-[3/4] overflow-hidden bg-[var(--appkit-color-surface)]",
509
- /** Wide image wrapper — video/landscape ratio */
510
- imageWide: "relative aspect-video overflow-hidden bg-[var(--appkit-color-surface)]",
511
- /** Square image wrapper */
512
- imageSquare: "relative aspect-square overflow-hidden bg-[var(--appkit-color-surface)]",
513
- /** Banner aspect ratio — 4:3 on mobile, auto on md+ */
514
- aspectBanner: "aspect-[4/3] md:aspect-auto",
515
- /** Card body — flex-column, responsive padding */
516
- body: "flex flex-col flex-1 gap-1 p-3 sm:p-4",
517
- /** Card footer — responsive padding */
518
- footer: "px-3 pb-3 sm:px-4 sm:pb-4",
519
- /** Badge overlay (top-left) */
520
- badge: "absolute top-2 left-2 z-10",
521
- /** Action overlay, e.g. wishlist button (top-right) */
522
- action: "absolute top-2 right-2 z-10",
523
- };
524
- /**
525
- * Flex layout compositions — pre-composed flex containers.
526
- * Use these instead of writing flex classes inline.
527
- */
528
- const FLEX = {
529
- row: "flex flex-row",
530
- col: "flex flex-col",
531
- rowWrap: "flex flex-row flex-wrap",
532
- colWrap: "flex flex-col flex-wrap",
533
- inline: "inline-flex",
534
- center: "flex items-center justify-center",
535
- between: "flex items-center justify-between",
536
- betweenStart: "flex items-start justify-between",
537
- betweenEnd: "flex items-end justify-between",
538
- start: "flex items-center justify-start",
539
- end: "flex items-center justify-end",
540
- rowCenter: "flex flex-row items-center",
541
- rowStart: "flex flex-row items-start",
542
- rowEnd: "flex flex-row items-end",
543
- centerCol: "flex flex-col items-center justify-center",
544
- colStart: "flex flex-col items-start",
545
- colCenter: "flex flex-col items-center",
546
- colEnd: "flex flex-col items-end",
547
- colBetween: "flex flex-col justify-between",
548
- inlineCenter: "inline-flex items-center",
549
- inlineFull: "inline-flex items-center justify-center",
550
- grow: "flex-1",
551
- growMin: "flex-1 min-w-0",
552
- growMinH: "flex-1 min-h-0",
553
- noShrink: "flex-shrink-0",
554
- none: "flex-none",
555
- hCenter: "flex justify-center",
556
- };
557
- /**
558
- * Position utilities.
559
- */
560
- const POSITION = {
561
- relative: "relative",
562
- absolute: "absolute",
563
- fixed: "fixed",
564
- sticky: "sticky",
565
- static: "static",
566
- fill: "absolute inset-0",
567
- absoluteCenter: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
568
- absoluteTop: "absolute top-0 inset-x-0",
569
- absoluteBottom: "absolute bottom-0 inset-x-0",
570
- absoluteTopRight: "absolute top-0 right-0",
571
- absoluteTopLeft: "absolute top-0 left-0",
572
- absoluteBottomRight: "absolute bottom-0 right-0",
573
- absoluteBottomLeft: "absolute bottom-0 left-0",
574
- };
575
- /**
576
- * Component state modifiers.
577
- */
578
- const STATES = {
579
- disabled: "opacity-50 cursor-not-allowed pointer-events-none",
580
- loading: "opacity-75 cursor-wait",
581
- readonly: "bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface-elevated)] cursor-default",
582
- error: "border-red-500 dark:border-red-500",
583
- success: "border-green-500 dark:border-green-500",
584
- focus: "focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:focus:ring-secondary-400 dark:focus:ring-offset-[var(--appkit-color-bg)]",
585
- };
586
- /**
587
- * Transition classes.
588
- */
589
- const TRANSITIONS = {
590
- default: "transition-all duration-200 ease-in-out",
591
- fast: "transition-all duration-150 ease-in-out",
592
- slow: "transition-all duration-500 ease-in-out",
593
- colors: "transition-colors duration-200 ease-in-out",
594
- transform: "transition-transform duration-200 ease-in-out",
595
- opacity: "transition-opacity duration-200 ease-in-out",
596
- };
597
- /**
598
- * Skeleton loading placeholders.
599
- *
600
- * Two variants:
601
- * - `pulse` (default) — fade in/out via `animate-pulse`.
602
- * - `shimmer` — left-to-right highlight sweep. Requires `animate-shimmer`
603
- * keyframe defined in tailwind.config.js:
604
- * ```js
605
- * // tailwind.config.js
606
- * theme: { extend: { animation: { shimmer: "shimmer 1.5s infinite" },
607
- * keyframes: { shimmer: { "0%": { backgroundPosition: "-400% 0" },
608
- * "100%": { backgroundPosition: "400% 0" } } } } }
609
- * ```
610
- */
611
- const SKELETON = {
612
- // - Pulse (fade) variant ----------------------------------------------------------------
613
- base: "animate-pulse rounded bg-zinc-200 bg-[var(--appkit-color-border-subtle)]",
614
- text: "animate-pulse rounded bg-zinc-200 bg-[var(--appkit-color-border-subtle)] h-4",
615
- heading: "animate-pulse rounded bg-zinc-200 bg-[var(--appkit-color-border-subtle)] h-7",
616
- image: "animate-pulse rounded-xl bg-zinc-200 bg-[var(--appkit-color-border-subtle)]",
617
- card: "animate-pulse rounded-2xl bg-zinc-200 bg-[var(--appkit-color-border-subtle)]",
618
- // - Shimmer (sweep) variant ----------------------------------------------------------
619
- shimmer: "animate-shimmer bg-[length:400%_100%] rounded bg-gradient-to-r from-zinc-200 via-zinc-100 to-zinc-200 from-[var(--appkit-color-border-subtle)] via-[var(--appkit-color-bg)] to-[var(--appkit-color-border-subtle)]",
620
- shimmerText: "animate-shimmer bg-[length:400%_100%] h-4 rounded bg-gradient-to-r from-zinc-200 via-zinc-100 to-zinc-200 from-[var(--appkit-color-border-subtle)] via-[var(--appkit-color-bg)] to-[var(--appkit-color-border-subtle)]",
621
- shimmerCard: "animate-shimmer bg-[length:400%_100%] rounded-2xl bg-gradient-to-r from-zinc-200 via-zinc-100 to-zinc-200 from-[var(--appkit-color-border-subtle)] via-[var(--appkit-color-bg)] to-[var(--appkit-color-border-subtle)]",
622
- };
623
- /**
624
- * Touch target sizes — WCAG 2.5.5 (minimum 44 × 44 px).
625
- */
626
- const TOUCH = {
627
- target: "min-h-[44px] min-w-[44px]",
628
- targetSm: "min-h-[36px] min-w-[36px]",
629
- targetH: "min-h-[44px]",
630
- targetW: "min-w-[44px]",
631
- /** 44 px height primary button */
632
- button: "h-11 px-5 py-2.5",
633
- /** 44×44 icon button */
634
- icon: "h-11 w-11 flex items-center justify-center",
635
- /** 44 px height input */
636
- input: "h-11 px-3 py-2",
637
- /** 48 px height tab */
638
- tab: "h-12 px-4",
639
- /** Secondary chip (smaller but acceptable) */
640
- chip: "h-9 px-3",
641
- };
642
- /**
643
- * Scrollbar and safe-area utilities.
644
- */
645
- const UTILITIES = {
646
- scrollbarHide: "scrollbar-hide [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]",
647
- scrollbarThin: "scrollbar-thin [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-[var(--appkit-color-surface)] dark:[&::-webkit-scrollbar-track]:bg-slate-800/60 [&::-webkit-scrollbar-track]:rounded-full [&::-webkit-scrollbar-thumb]:bg-zinc-300 dark:[&::-webkit-scrollbar-thumb]:bg-slate-600 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb:hover]:bg-zinc-400 dark:[&::-webkit-scrollbar-thumb:hover]:bg-slate-500",
648
- scrollbarThinX: "[&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar-track]:rounded-full [&::-webkit-scrollbar-track]:bg-[var(--appkit-color-surface)] dark:[&::-webkit-scrollbar-track]:bg-slate-800/60 [&::-webkit-scrollbar-thumb]:bg-zinc-300 dark:[&::-webkit-scrollbar-thumb]:bg-slate-600 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb:hover]:bg-zinc-400 dark:[&::-webkit-scrollbar-thumb:hover]:bg-slate-500",
649
- safeAreaBottom: "pb-[env(safe-area-inset-bottom)]",
650
- safeAreaTop: "pt-[env(safe-area-inset-top)]",
651
- safeAreaInline: "px-[env(safe-area-inset-left)] px-[env(safe-area-inset-right)]",
652
- gradientText: "bg-gradient-to-r from-primary-600 to-primary-800 bg-clip-text text-transparent",
653
- /** Text truncation utilities */
654
- textTruncate: "truncate",
655
- textClamp1: "line-clamp-1",
656
- textClamp2: "line-clamp-2",
657
- textClamp3: "line-clamp-3",
658
- textClamp4: "line-clamp-4",
659
- };
660
- /**
661
- * Common component pattern compositions.
662
- */
663
- const PATTERNS = {
664
- pageContainer: "min-h-screen bg-[var(--appkit-color-bg)]",
665
- sectionContainer: "max-w-7xl mx-auto px-4 md:px-6 lg:px-8 py-8 lg:py-12",
666
- formContainer: "bg-[var(--appkit-color-surface)] rounded-2xl border border-[var(--appkit-color-border)] shadow-sm p-6 lg:p-8 space-y-6",
667
- listItem: "bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] rounded-xl p-4 hover:border-[var(--appkit-color-border)] transition-colors",
668
- linkDefault: "text-primary-600 dark:text-secondary-400 hover:text-primary-500 dark:hover:text-secondary-300 transition-colors underline-offset-2 hover:underline",
669
- iconButton: "p-2 rounded-lg transition-colors hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)] active:bg-zinc-200 dark:active:bg-slate-700 min-h-[44px] min-w-[44px] flex items-center justify-center",
670
- modalOverlay: "fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50",
671
- modalContent: "bg-[var(--appkit-color-surface)] rounded-2xl border border-[var(--appkit-color-border)] shadow-xl max-w-lg w-full mx-4 overflow-hidden",
672
- divider: "border-t border-[var(--appkit-color-border)]",
673
- emptyState: "flex flex-col items-center justify-center py-12 text-center text-[var(--appkit-color-text-muted)]",
674
- };
675
- /** Icon colour variants and sizes. */
676
- const ICON = {
677
- muted: "text-[var(--appkit-color-text-faint)]",
678
- primary: "text-primary-600 dark:text-secondary-400",
679
- success: "text-emerald-500 dark:text-emerald-400",
680
- danger: "text-red-500 dark:text-red-400",
681
- warning: "text-amber-500 dark:text-amber-400",
682
- size: {
683
- sm: "w-4 h-4",
684
- md: "w-6 h-6",
685
- lg: "w-8 h-8",
686
- xl: "w-16 h-16",
687
- },
688
- };
689
- /** Horizontal tab active / inactive states. */
690
- const TAB = {
691
- active: "border-b-2 border-primary-600 dark:border-secondary-400 text-primary-600 dark:text-secondary-400 font-semibold",
692
- inactive: "border-transparent text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)]",
693
- };
694
- /**
695
- * Motion tokens — respect prefers-reduced-motion via motion-safe: prefix.
696
- * Custom keyframes must be defined in tailwind.config.js:
697
- * fade-in, slide-up, scale-in
698
- */
699
- const MOTION = {
700
- fadeIn: "motion-safe:animate-fade-in",
701
- slideUp: "motion-safe:animate-slide-up",
702
- scaleIn: "motion-safe:animate-scale-in",
703
- skeleton: "motion-safe:animate-pulse",
704
- transition: "motion-safe:transition-all motion-safe:duration-200",
705
- transitionSlow: "motion-safe:transition-all motion-safe:duration-500 motion-safe:ease-in-out",
706
- };
707
- /**
708
- * Mobile-first typography scale — maps to named semantic sizes.
709
- * Use in place of raw text-* utilities.
710
- */
711
- const TEXT = {
712
- h1: "text-2xl sm:text-3xl xl:text-4xl font-bold tracking-tight leading-tight",
713
- h2: "text-xl sm:text-2xl xl:text-3xl font-semibold tracking-tight",
714
- h3: "text-lg sm:text-xl xl:text-2xl font-semibold",
715
- h4: "text-base sm:text-lg font-semibold",
716
- body: "text-sm sm:text-base leading-relaxed",
717
- bodyLg: "text-base sm:text-lg leading-relaxed",
718
- small: "text-xs sm:text-sm",
719
- label: "text-xs font-medium uppercase tracking-wide",
720
- price: "text-lg sm:text-xl font-bold tabular-nums",
721
- priceLg: "text-2xl sm:text-3xl font-bold tabular-nums",
722
- muted: "text-[var(--appkit-color-text-muted)]",
723
- error: "text-red-600 dark:text-red-400",
724
- success: "text-green-600 dark:text-green-400",
725
- };
726
- /** Star rating colours. */
727
- const RATING = {
728
- filled: "text-yellow-400",
729
- empty: "text-[var(--appkit-color-text-faint)]",
730
- };
731
278
  // THEME_CONSTANTS deleted 2026-06-17 in Phase 8 of the Three-Layer Style
732
279
  // System refactor. Use primitive variant props (<Card>, <Section>, <Stack>,
733
280
  // <Text>, <Heading>, <Badge>, …) for every styling intent. The handful of
734
281
  // legacy raw-className tokens still needed live in typed feature files
735
- // under `_internal/shared/styles/{page,themed,grid,skeleton}.ts`. The
736
- // individual token groups (THEMED, LAYOUT, TYPOGRAPHY, SPACING, GRID, PAGE,
737
- // INPUT, CARD, FLEX, POSITION, STATES, TRANSITIONS, SKELETON, MOTION, TEXT,
738
- // TOUCH, FLUID_GRID, UTILITIES, PATTERNS, ICON, TAB, RATING) remain exported
739
- // individually above for any caller that still needs a slice of the old map.
282
+ // under `_internal/shared/styles/{page,themed,grid,skeleton}.ts`. The two
283
+ // token groups still consumed outside this file (LAYOUT, FLUID_GRID_MIN_WIDTHS)
284
+ // remain exported above. The rest of the old THEME_CONSTANTS slice (THEMED,
285
+ // TYPOGRAPHY, SPACING, GRID, PAGE, INPUT, CARD, FLEX, POSITION, STATES,
286
+ // TRANSITIONS, SKELETON, MOTION, TEXT, TOUCH, FLUID_GRID, UTILITIES,
287
+ // PATTERNS, ICON, TAB, RATING) had no remaining callers and was removed.
@@ -144,27 +144,6 @@ pageSize: pageSizeProp, stickyHeader: stickyHeaderProp, striped: stripedProp, sh
144
144
  ? col.render(item)
145
145
  : (item[col.key] ?? "-") }, col.key))), actions && (_jsx("td", { className: "appkit-data-table__td appkit-data-table__td--actions", onClick: (e) => e.stopPropagation(), children: actions(item) }))] }, keyExtractor(item)))) })] }) }) })), !externalPagination && totalPages > 1 && (_jsx(Div, { className: "appkit-data-table__pagination", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: setCurrentPage, maxVisible: resolvedPag.maxVisible, showFirstLast: resolvedPag.showFirstLast, showPrevNext: resolvedPag.showPrevNext, size: resolvedPag.size }) }))] }));
146
146
  }
147
- function DataTableViewToggle({ showViewToggle, showTableView, activeViewMode, labels, onViewModeChange, }) {
148
- if (!showViewToggle)
149
- return null;
150
- return (_jsxs(Div, { className: "appkit-data-table__view-toggle", role: "toolbar", "aria-label": "View mode", children: [showTableView && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onViewModeChange("table"), "aria-label": labels.tableView, "aria-pressed": activeViewMode === "table", className: `appkit-data-table__view-btn ${activeViewMode === "table" ? "appkit-data-table__view-btn--active" : ""} hidden sm:flex`, children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 10h18M3 6h18M3 14h18M3 18h18" }) }) })), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onViewModeChange("grid"), "aria-label": labels.gridView, "aria-pressed": activeViewMode === "grid", className: `appkit-data-table__view-btn ${activeViewMode === "grid" ? "appkit-data-table__view-btn--active" : ""}`, children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z" }) }) }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onViewModeChange("list"), "aria-label": labels.listView, "aria-pressed": activeViewMode === "list", className: `appkit-data-table__view-btn ${activeViewMode === "list" ? "appkit-data-table__view-btn--active" : ""}`, children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" }) }) })] }));
151
- }
152
- function DataTableTableView({ columns, paginatedData, keyExtractor, selectable, selectedIds, stickyHeader, striped, sortKey, sortDirection, actions, labelActions, onRowClick, onSelectionChange, onSort, onRowSelectionChange, }) {
153
- return (_jsx(Div, { className: "appkit-data-table__wrapper", children: _jsx(Div, { className: `appkit-data-table__scroll ${stickyHeader ? "appkit-data-table__scroll--sticky" : ""}`, children: _jsxs("table", { className: "appkit-data-table__table", children: [_jsx("thead", { className: `appkit-data-table__thead ${stickyHeader ? "appkit-data-table__thead--sticky" : ""}`, children: _jsxs("tr", { children: [selectable && (_jsx("th", { scope: "col", className: "px-4 py-3 w-8", children: _jsx("input", { type: "checkbox", className: CLS_CHECKBOX, "aria-label": "Select all on page", checked: paginatedData.length > 0 && paginatedData.every((item) => selectedIds.includes(keyExtractor(item))), onChange: (e) => {
154
- const pageIds = paginatedData.map(keyExtractor);
155
- onSelectionChange?.(e.target.checked
156
- ? [...new Set([...selectedIds, ...pageIds])]
157
- : selectedIds.filter((id) => !pageIds.includes(id)));
158
- } }) })), columns.map((col) => (_jsx("th", { scope: "col", "aria-sort": col.sortable
159
- ? sortKey === col.key
160
- ? sortDirection === "asc" ? "ascending" : "descending"
161
- : "none"
162
- : undefined, className: `appkit-data-table__th ${col.sortable ? "appkit-data-table__th--sortable" : ""}`, style: { width: col.width }, onClick: () => col.sortable && onSort(col.key), children: _jsxs(Row, { gap: "sm", children: [col.header, col.sortable && (_jsx(Span, { "aria-hidden": "true", color: "faint", children: sortKey === col.key ? (sortDirection === "asc" ? "↑" : "↓") : _jsx(Span, { className: "opacity-30", children: "\u2195" }) }))] }) }, col.key))), actions && (_jsx("th", { scope: "col", className: "appkit-data-table__th appkit-data-table__td--actions", children: labelActions }))] }) }), _jsx("tbody", { className: "appkit-data-table__tbody divide-y divide-zinc-200 divide-[var(--appkit-color-border)]", children: paginatedData.map((item, index) => (_jsxs("tr", { className: [
163
- "appkit-data-table__row",
164
- striped && index % 2 === 1 ? "appkit-data-table__row--striped" : "",
165
- onRowClick ? "appkit-data-table__row--clickable" : "",
166
- ].join(" "), onClick: () => onRowClick?.(item), children: [selectable && (_jsx("td", { className: "px-4 py-4 w-8", onClick: (e) => e.stopPropagation(), children: _jsx("input", { type: "checkbox", className: CLS_CHECKBOX, "aria-label": "Select row", checked: selectedIds.includes(keyExtractor(item)), onChange: (e) => onRowSelectionChange(item, e.target.checked) }) })), columns.map((col) => (_jsx("td", { className: "appkit-data-table__td", children: col.render ? col.render(item) : (item[col.key] ?? "-") }, col.key))), actions && (_jsx("td", { className: "appkit-data-table__td appkit-data-table__td--actions", onClick: (e) => e.stopPropagation(), children: actions(item) }))] }, keyExtractor(item)))) })] }) }) }));
167
- }
168
147
  function SelectableCard({ id, selectable, selected, onToggle, children, listMode = false, }) {
169
148
  if (!selectable)
170
149
  return _jsx(Div, { className: "h-full", children: children });
@@ -12,7 +12,9 @@ export type AnchorGap = "none" | "2xs" | "xs" | "sm" | "md" | "lg";
12
12
  * `href` shape (`http(s)://…`, `mailto:`, `tel:`, `#fragment` → `Anchor`;
13
13
  * everything else → `TextLink`).
14
14
  */
15
- export type AnchorTone = "default" | "muted" | "brand" | "accent" | "danger" | "success" | "inverse";
15
+ export type AnchorTone = "default" | "muted" | "brand" | "accent" | "danger" | "success" | "inverse"
16
+ /** No colour applied — for card/button-style anchors that fully own their own colour via `className`. */
17
+ | "none";
16
18
  export type AnchorUnderline = "none" | "hover" | "always";
17
19
  export type AnchorSize = "xs" | "sm" | "base" | "lg";
18
20
  export interface AnchorProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
@@ -26,6 +26,7 @@ const TONE_CLS = {
26
26
  danger: "text-[var(--appkit-color-error)] hover:text-[var(--appkit-color-error-hover)]",
27
27
  success: "text-[var(--appkit-color-success)]",
28
28
  inverse: "text-[var(--appkit-color-text-on-primary)]",
29
+ none: "",
29
30
  };
30
31
  const UNDERLINE_CLS = {
31
32
  none: "no-underline",
@@ -1,6 +1,9 @@
1
1
  /* Avatar — circular image or initials display with size variants */
2
2
 
3
3
  .appkit-avatar {
4
+ position: relative;
5
+ display: inline-block;
6
+ overflow: hidden;
4
7
  border-radius: 9999px;
5
8
  object-fit: cover;
6
9
  background: var(--appkit-color-zinc-100);
@@ -2,11 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronRight } from "lucide-react";
3
3
  import { Nav, Ol, Li } from "./Semantic";
4
4
  import { Span } from "./Typography";
5
+ import { TextLink } from "./TextLink";
5
6
  export function Breadcrumb({ items, className = "" }) {
6
7
  return (_jsx(Nav, { "aria-label": "Breadcrumb", className: `appkit-breadcrumb ${className}`, children: _jsx(Ol, { className: "appkit-breadcrumb__list", children: items.map((item, i) => {
7
8
  const isLast = i === items.length - 1;
8
9
  return (_jsxs(Li, { className: "appkit-breadcrumb__item", children: [i > 0 && (_jsx(ChevronRight, { className: "appkit-breadcrumb__separator", "aria-hidden": "true" })), isLast || !item.href ? (_jsx(Span, { className: isLast
9
10
  ? "appkit-breadcrumb__current"
10
- : "appkit-breadcrumb__text", "aria-current": isLast ? "page" : undefined, children: item.label })) : (_jsx("a", { href: item.href, className: "appkit-breadcrumb__link", children: item.label }))] }, i));
11
+ : "appkit-breadcrumb__text", "aria-current": isLast ? "page" : undefined, children: item.label })) : (_jsx(TextLink, { href: item.href, variant: "none", className: "appkit-breadcrumb__link", children: item.label }))] }, i));
11
12
  }) }) }));
12
13
  }
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  import { ChevronRight } from "lucide-react";
5
5
  import { Button } from "./Button";
6
+ import { TextLink } from "./TextLink";
6
7
  export function FormActionBar({ title, breadcrumbs, onSaveDraft, onPublish, publishLabel = "Publish", onPreview, onDiscard, isDirty, isSubmitting, className, }) {
7
- return (_jsxs("div", { className: `appkit-form-action-bar${className ? ` ${className}` : ""}`, children: [_jsxs("div", { className: "appkit-form-action-bar__meta", children: [breadcrumbs && breadcrumbs.length > 0 && (_jsx("div", { className: "appkit-form-action-bar__breadcrumbs", children: breadcrumbs.map((crumb, i) => (_jsxs(React.Fragment, { children: [i > 0 && _jsx(ChevronRight, { className: "h-3 w-3 flex-shrink-0" }), crumb.href ? (_jsx("a", { href: crumb.href, className: "hover:underline truncate", children: crumb.label })) : (_jsx("span", { className: "truncate", children: crumb.label }))] }, i))) })), title && (_jsxs("div", { className: "appkit-form-action-bar__title", children: [isDirty && (_jsx("span", { className: "appkit-form-action-bar__dirty-dot mr-1.5", "aria-label": "Unsaved changes" })), title] }))] }), _jsxs("div", { className: "appkit-form-action-bar__actions", children: [isDirty && onDiscard && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSubmitting, children: "Discard" })), onPreview && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onPreview, disabled: isSubmitting, children: "Preview" })), onSaveDraft && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onSaveDraft, isLoading: isSubmitting, disabled: isSubmitting, children: "Save draft" })), _jsx(Button, { type: "button", size: "sm", onClick: onPublish, isLoading: isSubmitting, disabled: isSubmitting, children: publishLabel })] })] }));
8
+ return (_jsxs("div", { className: `appkit-form-action-bar${className ? ` ${className}` : ""}`, children: [_jsxs("div", { className: "appkit-form-action-bar__meta", children: [breadcrumbs && breadcrumbs.length > 0 && (_jsx("div", { className: "appkit-form-action-bar__breadcrumbs", children: breadcrumbs.map((crumb, i) => (_jsxs(React.Fragment, { children: [i > 0 && _jsx(ChevronRight, { className: "h-3 w-3 flex-shrink-0" }), crumb.href ? (_jsx(TextLink, { href: crumb.href, variant: "inherit", truncate: true, children: crumb.label })) : (_jsx("span", { className: "truncate", children: crumb.label }))] }, i))) })), title && (_jsxs("div", { className: "appkit-form-action-bar__title", children: [isDirty && (_jsx("span", { className: "appkit-form-action-bar__dirty-dot mr-1.5", "aria-label": "Unsaved changes" })), title] }))] }), _jsxs("div", { className: "appkit-form-action-bar__actions", children: [isDirty && onDiscard && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSubmitting, children: "Discard" })), onPreview && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onPreview, disabled: isSubmitting, children: "Preview" })), onSaveDraft && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onSaveDraft, isLoading: isSubmitting, disabled: isSubmitting, children: "Save draft" })), _jsx(Button, { type: "button", size: "sm", onClick: onPublish, isLoading: isSubmitting, disabled: isSubmitting, children: publishLabel })] })] }));
8
9
  }
@@ -69,4 +69,4 @@ export interface ListingLayoutProps {
69
69
  /** When provided, the entire layout is replaced with this view (detail/edit mode). */
70
70
  detailView?: ReactNode;
71
71
  }
72
- export declare function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen, filterPendingCount, className, loading, errorSlot, labels, portal, overlays, detailView, }: ListingLayoutProps): import("react").JSX.Element;
72
+ export declare function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen, filterPendingCount, className, loading: _loading, errorSlot, labels, portal, overlays, detailView, }: ListingLayoutProps): import("react").JSX.Element;
@@ -35,7 +35,7 @@ const DEFAULT_LABELS = {
35
35
  hideFilters: "Hide filters",
36
36
  filterActiveCount: (count) => `${count} active`,
37
37
  };
38
- export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount = 0, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount = 0, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen = false, filterPendingCount, className = "", loading = false, errorSlot, labels, portal, overlays, detailView, }) {
38
+ export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount = 0, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount = 0, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen = false, filterPendingCount, className = "", loading: _loading = false, errorSlot, labels, portal, overlays, detailView, }) {
39
39
  // useState must run unconditionally on every render — these were
40
40
  // previously declared after the detail-view early return below, which
41
41
  // skipped both hook calls whenever `detailView` was passed, violating the
@@ -10,7 +10,7 @@ function useMotionProps(props) {
10
10
  const reduced = useReducedMotion();
11
11
  if (!reduced)
12
12
  return props;
13
- const { initial, animate, exit, whileHover, whileTap, whileInView, transition, ...rest } = props;
13
+ const { initial: _initial, animate: _animate, exit: _exit, whileHover: _whileHover, whileTap: _whileTap, whileInView: _whileInView, transition: _transition, ...rest } = props;
14
14
  return { ...rest, initial: false, animate: false };
15
15
  }
16
16
  export function FadeIn({ delay = 0, duration = 0.3, children, ...props }) {
@@ -6,6 +6,7 @@ import { Button } from "./Button";
6
6
  import { Div } from "./Div";
7
7
  import { Heading } from "./Typography";
8
8
  import { Row } from "./Layout";
9
+ import { TextLink } from "./TextLink";
9
10
  export function QuickCreateModal({ isOpen, title, onSave, onSaved, onCancel, children, fullPageHref, saveLabel = "Save", cancelLabel = "Cancel", isSaving = false, saveDisabled = false, }) {
10
11
  const [busy, setBusy] = React.useState(false);
11
12
  const titleId = React.useId();
@@ -44,5 +45,5 @@ export function QuickCreateModal({ isOpen, title, onSave, onSaved, onCancel, chi
44
45
  if (!isOpen || typeof document === "undefined")
45
46
  return null;
46
47
  const saving = busy || isSaving;
47
- return createPortal(_jsxs(Div, { className: "appkit-quick-create-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [_jsx(Div, { className: "appkit-quick-create-modal__backdrop", "aria-hidden": "true", onClick: () => !saving && onCancel() }), _jsxs(Div, { ref: panelRef, tabIndex: -1, className: "appkit-quick-create-modal__panel", children: [_jsxs(Div, { className: "appkit-quick-create-modal__header", children: [_jsx(Heading, { level: 2, id: titleId, className: "appkit-quick-create-modal__title", children: title }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onCancel, "aria-label": "Close", disabled: saving, children: "\u00D7" })] }), _jsx(Div, { className: "appkit-quick-create-modal__body", children: children }), _jsxs(Div, { className: "appkit-quick-create-modal__footer", children: [fullPageHref ? (_jsx("a", { href: fullPageHref, target: "_blank", rel: "noreferrer", className: "appkit-quick-create-modal__full-link", children: "Add more details \u2192" })) : (_jsx("span", {})), _jsxs(Row, { className: "appkit-quick-create-modal__actions", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: onCancel, disabled: saving, children: cancelLabel }), _jsx(Button, { variant: "primary", type: "button", onClick: handleSave, disabled: saving || saveDisabled, isLoading: saving, children: saveLabel })] })] })] })] }), document.body);
48
+ return createPortal(_jsxs(Div, { className: "appkit-quick-create-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [_jsx(Div, { className: "appkit-quick-create-modal__backdrop", "aria-hidden": "true", onClick: () => !saving && onCancel() }), _jsxs(Div, { ref: panelRef, tabIndex: -1, className: "appkit-quick-create-modal__panel", children: [_jsxs(Div, { className: "appkit-quick-create-modal__header", children: [_jsx(Heading, { level: 2, id: titleId, className: "appkit-quick-create-modal__title", children: title }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onCancel, "aria-label": "Close", disabled: saving, children: "\u00D7" })] }), _jsx(Div, { className: "appkit-quick-create-modal__body", children: children }), _jsxs(Div, { className: "appkit-quick-create-modal__footer", children: [fullPageHref ? (_jsx(TextLink, { variant: "none", href: fullPageHref, target: "_blank", rel: "noreferrer", className: "appkit-quick-create-modal__full-link", children: "Add more details \u2192" })) : (_jsx("span", {})), _jsxs(Row, { className: "appkit-quick-create-modal__actions", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: onCancel, disabled: saving, children: cancelLabel }), _jsx(Button, { variant: "primary", type: "button", onClick: handleSave, disabled: saving || saveDisabled, isLoading: saving, children: saveLabel })] })] })] })] }), document.body);
48
49
  }