Skip to content

feat(maz-ui): add local-flags prop for offline/hybrid app support#1576

Open
LouisMazel wants to merge 32 commits into
developfrom
feat/1196-local-flags
Open

feat(maz-ui): add local-flags prop for offline/hybrid app support#1576
LouisMazel wants to merge 32 commits into
developfrom
feat/1196-local-flags

Conversation

@LouisMazel

Copy link
Copy Markdown
Owner

Summary

  • Adds a localFlags: boolean prop (default false) on MazSelectCountry and MazInputPhoneNumber
  • When local-flags is enabled, flags are rendered via LazyMazFlagXX components from @maz-ui/icons (already a dependency) — no CDN request
  • Falls back to a colored badge when no bundled SVG asset exists for a code (e.g. gb-eng, sub-national codes)
  • CDN behavior (flagcdn.com) is completely unchanged when local-flags=false (default)

Motivation

Closes #1196 — hybrid apps (Capacitor, Cordova) may run without network access. Flags sourced from flagcdn.com would fail to render in offline scenarios. This feature allows bundling flag assets locally.

Changes

File What
MazSelectCountry.vue localFlags prop + getLocalFlagComponent() using @maz-ui/icons
MazInputPhoneNumber.vue localFlags prop forwarded to inner MazSelectCountry
MazSelectCountry.spec.ts New tests for all localFlags scenarios
MazInputPhoneNumber.spec.ts Tests for prop propagation
maz-select-country.md Documentation with usage example
maz-input-phone-number.md Documentation with usage example

Technical decisions

  • Object.hasOwn(icons, key) is used instead of direct bracket access to avoid triggering Vitest's strict mock Proxy get trap when a key is absent
  • The check for each component exists in the template (getLocalFlagComponent(option.code)) — this is synchronous since the icons are markRaw(defineAsyncComponent(...)) objects that exist immediately

Test plan

  • pnpm test:unit:all — 3070 tests, all pass
  • pnpm lint:js — no errors
  • pnpm typecheck:all — no errors
  • localFlags=false (default): CDN flags still rendered via MazLazyImg
  • localFlags=true + known code (e.g. FR): local flag component rendered
  • localFlags=true + unknown sub-national code (e.g. gb-eng): colored badge shown, no CDN request
  • Prop correctly propagated from MazInputPhoneNumberMazSelectCountry

LouisMazel and others added 26 commits May 5, 2026 23:54
Maz-UI v5 is a focused major: a theming overhaul on top of Tailwind v4,
RTL-correct components, a lighter chart, a simpler icon API, and a one-shot
CLI to make the upgrade itself painless.

**Highlights**

- **Theming, top to bottom** — OKLCh color system for perceptually uniform
  scales, honest token names (`background` → `surface`, `border` → `divider`),
  a real radius scale, a `components` block to override per-component tokens,
  the new `nova` preset, and preset persistence across reloads.
- **Logical direction by default** — every `left`/`right` prop, slot and CSS
  hook is now `start`/`end`. Set `dir="rtl"` and components mirror correctly.
- **Lighter chart** — `MazChart` drops `vue-chartjs`, lazy-loads `chart.js`,
  and only registers the modules the chart `type` actually needs.
- **Simpler icons** — one `icon` prop instead of four. New
  `@maz-ui/icons/raw/*` subpath for raw SVG inlining without a Vue component.
- **Standardized sizes** — `MazBadge` joins the rest of the library on the
  `MazSize` keyword scale.
- **~8% lighter library bundle** thanks to the chart, icon and theming
  refactors.
- Modern browsers required (Chromium 111+, Safari 16.4+, Firefox 128+).

**Migration**

- **Mechanical part:** `npx @maz-ui/upgrade ./` rewrites your code, bumps
  every `maz-ui` / `@maz-ui/*` entry in `package.json` to `^5.0.0`, and runs
  the right `pnpm`/`yarn`/`bun`/`npm install` for you.
- **Judgment calls** (icon paths, theme preset reshape, chart animation
  default): connect [`@maz-ui/mcp`](https://maz-ui.com/guide/mcp) to your AI
  assistant and walk the guide section by section.

**Links**

- [Announcement](https://maz-ui.com/blog/v5)
- [Migration guide](https://maz-ui.com/guide/migration-v5)
- [`@maz-ui/upgrade` CLI](https://github.com/LouisMazel/maz-ui/tree/master/packages/upgrade)
- [`@maz-ui/mcp` server](https://maz-ui.com/guide/mcp)

> **Solo maintainer note:** v4 will receive no further support after v5
> stable — no security fixes, no backports.
Maz-UI v5 is a focused major: a theming overhaul on top of Tailwind v4,
RTL-correct components, a lighter chart, a simpler icon API, and a one-shot
CLI to make the upgrade itself painless.

**Highlights**

- **Theming, top to bottom** — OKLCh color system for perceptually uniform
  scales, honest token names (`background` → `surface`, `border` → `divider`),
  a real radius scale, a `components` block to override per-component tokens,
  the new `nova` preset, and preset persistence across reloads.
- **Logical direction by default** — every `left`/`right` prop, slot and CSS
  hook is now `start`/`end`. Set `dir="rtl"` and components mirror correctly.
- **Lighter chart** — `MazChart` drops `vue-chartjs`, lazy-loads `chart.js`,
  and only registers the modules the chart `type` actually needs.
- **Simpler icons** — one `icon` prop instead of four. New
  `@maz-ui/icons/raw/*` subpath for raw SVG inlining without a Vue component.
- **Standardized sizes** — `MazBadge` joins the rest of the library on the
  `MazSize` keyword scale.
- **~8% lighter library bundle** thanks to the chart, icon and theming
  refactors.
- Modern browsers required (Chromium 111+, Safari 16.4+, Firefox 128+).

**Migration**

- **Mechanical part:** `npx @maz-ui/upgrade ./` rewrites your code, bumps
  every `maz-ui` / `@maz-ui/*` entry in `package.json` to `^5.0.0`, and runs
  the right `pnpm`/`yarn`/`bun`/`npm install` for you.
- **Judgment calls** (icon paths, theme preset reshape, chart animation
  default): connect [`@maz-ui/mcp`](https://maz-ui.com/guide/mcp) to your AI
  assistant and walk the guide section by section.

**Links**

- [Announcement](https://maz-ui.com/blog/v5)
- [Migration guide](https://maz-ui.com/guide/migration-v5)
- [`@maz-ui/upgrade` CLI](https://github.com/LouisMazel/maz-ui/tree/master/packages/upgrade)
- [`@maz-ui/mcp` server](https://maz-ui.com/guide/mcp)

> **Solo maintainer note:** v4 will receive no further support after v5
> stable — no security fixes, no backports.
…d maz plugin

Ships a new ESLint plugin under the `maz/*` namespace, starting with `maz/tailwind-no-arbitrary-px` which forbids `px` units inside Tailwind arbitrary value classes (`w-[16px]`, `[gap:24px]`, …) and autofixes them to `rem` (or `em`).

The plugin and rule are registered automatically when `tailwindcss` is enabled in `defineConfig`. Configure via the `tailwindcss.noArbitraryPx` shortcut or via a standard ESLint `rules` override:

```ts
defineConfig({
  tailwindcss: {
    preset: 'recommended',
    noArbitraryPx: { baseFontSize: 16, unit: 'rem', severity: 'error' },
  },
})
```

`mazPlugin` is also exported for users who want to wire the rule manually without enabling the full Tailwind preset.
* feat(maz-ui): add MazSidebar component with sub-components and composable

Implements a fully accessible, composable sidebar system for dashboard,
admin, and SaaS applications. State is shared via provide/inject from
the root MazSidebar component.

Components added:
- MazSidebar: root container (push/overlay modes, offcanvas/icon/none collapsible)
- MazSidebarHeader, MazSidebarContent, MazSidebarFooter: layout zones
- MazSidebarGroup: grouping with optional label (sr-only in icon mode)
- MazSidebarSeparator: visual HR divider
- MazSidebarMenu / MazSidebarMenuItem: ul/li with ARIA roles
- MazSidebarMenuButton: link/button with icon, label, badge, tooltip auto-show
- MazSidebarMenuSub: collapsible sub-menu with ArrowRight/ArrowLeft keyboard support
- MazSidebarTrigger: toggle button with aria-expanded and aria-controls

Composable added:
- useMazSidebar: typed inject for open state, toggle, setOpen, state

Closes #1573

* refactor(maz-ui): polish MazSidebar to align with project conventions

- Rename `useMazSidebar` composable to `useSidebar` (kept Maz-prefixed types)
- Drop all scoped CSS in favour of Tailwind utilities and add `m-reset-css` on every sub-component root so theming and tree-shaking work as expected
- MazSidebarMenuButton: `icon` accepts `MazIconLike`, `badge` accepts `MazBadgeProps`, tooltip via `v-tooltip` directive
- MazSidebarMenuSub: smooth grid-rows expand/collapse via `MazExpandAnimation`, accepts `MazIconLike` icon
- MazSidebarTrigger: replace inline SVG with `MazIcon` + `MazBars3`
- Overlay mode delegated to `MazBackdrop` (reuses scroll-lock, focus trap, escape, click-outside)
- Fix offcanvas-collapsed border leak and the icon-centering jump during collapse via `min(calc((var(--maz-sidebar-icon-width)-1.25rem)/2), calc((100%-1.25rem)/2))` — icons stay centred regardless of any padding applied to `MazSidebarContent`
- Split the monolithic spec into one file per component (11 specs, 121 tests)
- Doc: replace inline `style` with Tailwind classes and document the new prop shapes
- Wire MazSidebar components and `useSidebar` into the Nuxt module auto-imports
)

* feat(maz-ui): add MazCodeHighlight component with shiki syntax highlighting

- Add MazCodeHighlight component with code/language/theme props
- Support default slot text extraction as fallback for code prop
- Auto-detect dark/light mode via MutationObserver on html element
- Lazy-load shiki via dynamic import to keep bundle lean
- Add shiki ^3.0.0 to lib dependencies
- Export from components/index.ts

* feat(maz-ui): add MazWindowMockup component with browser/terminal/editor variants

- Three macOS-style traffic light buttons in all variant title bars
- Browser variant: address bar with url prop (default 'localhost')
- Editor variant: filename tab with filename prop (default 'index.vue')
- Terminal variant: centered title with title prop (default 'zsh'), $ prompt
- showPrompt prop and #prompt slot for terminal prompt customization
- minHeight prop for CSS-based content area height control
- code + language props trigger integrated MazCodeHighlight rendering
- Default slot for free content when code prop is not set
- Dark/light mode compatible via maz-ui CSS vars
- Export from components/index.ts

* feat(@maz-ui/nuxt): register MazCodeHighlight and MazWindowMockup in Nuxt module

Add MazCodeHighlight and MazWindowMockup to COMPONENT_NAMES in the Nuxt module
for auto-import support. MazComponentsResolver already resolves all Maz-prefixed
components via regex pattern.

* test(maz-ui): add unit tests for MazCodeHighlight and MazWindowMockup

- MazCodeHighlight: tests for code prop, slot fallback, language prop,
  theme prop, empty state, and shiki error fallback
- MazWindowMockup: tests for all three variants (browser/terminal/editor),
  url/filename/title props, showPrompt prop, prompt slot, default slot
- MazCodeHighlight stubbed in MazWindowMockup tests to avoid async teardown issues

* docs(maz-ui): add documentation pages for MazCodeHighlight and MazWindowMockup

- Add maz-code-highlight.md with basic usage, slot fallback, language,
  and custom theme examples
- Add maz-window-mockup.md with browser, terminal (with custom prompt),
  and editor variant examples
- Add both to the sidebar under a new 'Display' section in components.mts
…oneNumber

Adds a `localFlags` boolean prop (default: false) on both components.
When enabled, flags are rendered from bundled SVG assets via LazyMazFlagXX
components from @maz-ui/icons instead of requesting images from flagcdn.com.

Falls back to a colored badge when no bundled asset exists for a code
(e.g. gb-eng, sub-national codes). CDN behavior is unchanged by default.

Closes #1196
@LouisMazel

Copy link
Copy Markdown
Owner Author

🤖 [tech-lead]

Required Fixes

  1. [apps/docs/src/components/maz-select-country.md:332] — La démo utilise v-model="localFlagsExample" mais cette ref n'est jamais déclarée dans le <script setup> de la page (seul selectedCountry = ref('FR') existe ligne 34). La page de doc plantera au runtime (localFlagsExample is not defined ou binding silencieusement undefined). Soit ajouter const localFlagsExample = ref('FR') au script de la page, soit réutiliser selectedCountry existant.

  2. [packages/lib/src/components/MazSelectCountry.vue:9]import * as MazIcons from '@maz-ui/icons' combiné à un accès par clé dynamique (icons[key]) casse le tree-shaking sous Rollup/Vite. Le bundle de MazSelectCountry embarquera désormais les ~861 wrappers exportés par @maz-ui/icons/index.ts (dont les 513 LazyMazFlagXX), même pour les consommateurs qui n'activent jamais localFlags. C'est une régression non triviale pour tous les utilisateurs actuels du composant. Les autres composants du repo (cf. MazDropzone.vue, MazCheckbox.vue) importent chaque icône via son sous-chemin précis (@maz-ui/icons/lazy/MazXxx) — ce pattern doit être suivi ici aussi, par exemple via un defineAsyncComponent qui fait import('@maz-ui/icons/lazy/MazFlag' + key) à la demande, ou en n'évaluant la résolution que dans la branche localFlags=true. Le namespace import est à proscrire.

Nits (non bloquants)

  1. [MazSelectCountry.vue:269]code.toUpperCase().replace('-', '') ne supprime que le premier tiret. Aujourd'hui les codes pays ISO restent à un tiret max donc OK, mais préférer .replaceAll('-', '') (ou /-/g) pour être défensif si jamais des codes type xx-yy-zz étaient passés via customCountriesList.

  2. [MazSelectCountry.vue:268] — La justification Object.hasOwn "pour éviter le Proxy strict de Vitest" est fragile : dans la PR le mock de test (MazSelectCountry.spec.ts:7-13) est un objet littéral, pas un Proxy — un simple icons[key] ?? null fonctionnerait identiquement. Le pattern actuel n'est pas faux, mais le commentaire du PR body sur "Vitest strict Proxy" ne reflète pas la réalité du code testé.

Points positifs

  • Convention de commit conforme (feat(maz-ui): ...).
  • Couverture de tests complète sur les 4 scénarios (selector + liste × known/unknown code) + propagation depuis MazInputPhoneNumber.
  • Comportement par défaut (localFlags=false) strictement préservé — pas de régression CDN.
  • Périmètre maîtrisé : aucun refactor incident.

Merci de corriger les points 1 et 2 avant merge — le 1 casse la page de doc, le 2 alourdit le bundle pour 100 % des consommateurs.

@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 1.74kB (0.06%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
lib-esm 683.43kB 1.74kB (0.25%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: lib-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
chunks/MazInputPhoneNumber.*.js 84 bytes 12.6kB 0.67%
chunks/MazSelectCountry.*.js 1.65kB 8.78kB 23.16% ⚠️

Files in chunks/MazInputPhoneNumber.*.js:

  • ./src/components/MazInputPhoneNumber.vue → Total Size: 248 bytes

Files in chunks/MazSelectCountry.*.js:

  • ./src/components/MazSelectCountry.vue → Total Size: 239 bytes

@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.33333% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.41%. Comparing base (202e55d) to head (fea38e8).

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1576      +/-   ##
===========================================
+ Coverage    92.24%   92.41%   +0.16%     
===========================================
  Files          228      246      +18     
  Lines         8128     8319     +191     
  Branches      2179     2241      +62     
===========================================
+ Hits          7498     7688     +190     
- Misses         522      523       +1     
  Partials       108      108              
Flag Coverage Δ
eslint-config 100.00% <ø> (ø)
lib 90.72% <93.33%> (+<0.01%) ⬆️
mcp 95.63% <ø> (ø)
node 100.00% <ø> (ø)
nuxt 100.00% <ø> (?)
stylelint-config 100.00% <ø> (ø)
themes 99.80% <ø> (ø)
translations 95.80% <ø> (ø)
utils 94.73% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
maz-ui Library 90.72% <93.33%> (+<0.01%) ⬆️
Utils Package 94.73% <ø> (ø)
MCP Package 95.63% <ø> (ø)
Icons Package ∅ <ø> (∅)
Translations Package 95.80% <ø> (ø)
Node Package 100.00% <ø> (ø)
Nuxt Package 100.00% <ø> (∅)
Themes Package 99.80% <ø> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 202e55d...fea38e8. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Replace `import * as MazIcons from '@maz-ui/icons'` with on-demand
  dynamic imports from `@maz-ui/icons/lazy/<ComponentName>` to restore
  tree-shaking; results are cached in a shallowReactive map so each
  flag is fetched at most once per page lifecycle
- Update test mocks to match new per-module import paths
- Use replaceAll('-', '') instead of replace('-', '') to handle codes
  with multiple dashes (e.g. en-US-posix)
- Add missing `localFlagsExample` ref to VitePress demo script setup
@LouisMazel

Copy link
Copy Markdown
Owner Author

🤖 [dev]

Corrections du tech-lead review appliquées :

[high] Suppression de import * as MazIcons from '@maz-ui/icons'
Remplacé par des imports dynamiques à la demande depuis @maz-ui/icons/lazy/<ComponentName>. Un cache shallowReactive évite les imports redondants — chaque drapeau n'est chargé qu'une seule fois par cycle de page. Les mocks de test ont été mis à jour en conséquence.

[high] localFlagsExample ref manquante dans la démo VitePress
Ajout de const localFlagsExample = ref() dans le <script setup> de maz-select-country.md.

[low] .replace('-', '').replaceAll('-', '')
Traitement correct des codes à tirets multiples (ex. en-US-posix).

[low] Object.hasOwn / commentaire trompeur
Sans objet avec le nouveau pattern de cache — l'accès au module se fait via mod[componentName] ?? null.

@LouisMazel LouisMazel force-pushed the develop branch 5 times, most recently from f12c763 to 82851e1 Compare June 10, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant