Migration v1 → v2

A guide for those already using ozi-ui v1 who want to move to v2.

The v2 principle: ozi-ui depends on no third-party library. Pure JavaScript — jQuery survives only in the integrations/ layer (compatibility shims).

Good news: most of your code does not break. The data-ozi-* and data-zld-* attributes, the public API (OZI.components.* / window.OziX) and the zld* aliases keep working. What actually changes is listed below.


1. Versions

Versioning is major +1 per plugin — not a single version for everything.

Plugin v1 v2
ozi.js (index) 1.0.7 2.0.0 (OZI.version)
ozi-conf 2.0.x 3.0.0
ozi-loaddata 4.x 5.0.0
ozi-select 5.x 6.0.0
ozi-autocomplete · ozi-editor · ozi-auth · ozi-search · ozi-audio 3.x 4.0.0
ozi-editor-md 1.x 2.0.0
ozi-check · ozi-toggle 2.x 3.0.0
ozi-validate 1.x 2.1.0
ozi-actions · ozi-suggest 1.x 2.0.0
ozi-helpers 1.0.x 1.1.0

The Composer package publishes 2.0.0. v1 remains available under the v1-final tag.

composer require ozi-ui/core:^2.0

2. Zero jQuery — what to do

Before (v1): ozi.js shipped and used jQuery; many projects relied on the window.$ that "came along".

After (v2): the core does not use jQuery and does not load it at boot. It is still distributed at core/jquery-3.7.1.min.js for those who need it.

Action: if your app relied on the jQuery that came with ozi-ui, include it yourself:

<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>

3. The event contract — the most important change

In v1 events were dual-dispatch: $(el).trigger() (jQuery positional payload) and CustomEvent. In v2 it is CustomEvent only, with the payload in detail:

detail: { component: 'ozi-select', name: 'uf', value: 'SP', items: [...], source: 'user' }
// source: 'user' (user interaction) | 'api' (programmatic setValue)

Consuming ozi:change

// ❌ v1 — jQuery positional
$('[data-ozi-select="uf"]').on('ozi:change', function (e, items, instance, detail) {
    console.log(detail.value);
});

// ✅ v2 — native CustomEvent
document.addEventListener('ozi:change', function (e) {
    if (e.detail.component !== 'ozi-select') return;
    console.log(e.detail.name, e.detail.value);   // 'uf', 'SP'
});
<!-- ✅ v2 — Alpine -->
<div x-on:ozi:change="$wire.set('uf', $event.detail.value)"></div>

detail key changes

v1 v2
key name
instance removed — use OZI.components.select.get(el)
ozi-check: source (switch/group/item) levelsource is now 'user'|'api'
ozi-search value = query; keeps query/matched/total
ozi-auth new in v2 — now emits CustomEvent (was jQuery-only)

Every component now emits ozi:init (after initialization) and ozi:destroy.

Compatibility without rewriting (shims)

If you have consumers using the v1 positional format, enable the shim — it listens to the CustomEvent and re-emits it through jQuery in the old format. Nothing in your code has to change.

  • integrations/adapters/ozi-change-v1-compat.shim.js — positional ozi:change
  • integrations/adapters/ozi-check-v1-events.shim.jsoziCheck:initFetched

The shims are temporary and require jQuery. Move to addEventListener when you can.


4. Discontinued: ozi-copy and ozi-paste

Both were removed in v2. The replacements are short Alpine or vanilla JS recipes — with no icon-font dependency.

<!-- ozi-copy replacement -->
<button type="button"
        x-data="{ copied: false }"
        @click="navigator.clipboard.writeText('ABC-123').then(() => { copied = true; setTimeout(() => copied = false, 1500) })">
    <span x-text="copied ? '✓ copied' : 'Copy'"></span>
</button>

The v1 plugins remain available under the v1-final tag.

ozi-audio was NOT discontinued — it stays in scope and was migrated (v4.0.0).


5. Livewire

The adapter gained a native mode that respects wire:model modifiers:

<!-- Mode A (preferred): native dispatch — respects .live/.debounce/.lazy -->
<div wire:ignore>
    <div data-ozi-select="uf" data-ozi-livewire-native="#uf-hidden"></div>
</div>
<input id="uf-hidden" type="hidden" wire:model="uf">

<!-- Mode B (compat): component.set() -->
<div data-ozi-select="uf" data-ozi-livewire-model="uf"></div>
  • Anti-loop: the adapter ignores ozi:change with source: 'api' (a change coming from Livewire itself).
  • Re-init after morph: handled by ozi-hooks — nothing to do.
  • LW3 × LW4 guard is automatic.

6. Themes

A theme is data: 4 files linked in the <head> (tokens.css, overrides.css, dark.css, classmap.js). The same JS build serves all three themes — you only change oziConf({ theme }).

<link rel="stylesheet" href="/plugins/ozi-ui/themes/tailwind/tokens.css">
<link rel="stylesheet" href="/plugins/ozi-ui/themes/tailwind/overrides.css"><!-- new in v2 -->
<link rel="stylesheet" href="/plugins/ozi-ui/themes/tailwind/dark.css">
<script>oziConf({ theme: 'tailwind' });</script>

New in v2: themes/tailwind/overrides.css, which did not exist in v1. For a custom theme, copy themes/_template/.


7. Legacy aliases

They keep working, with a console warning when core.log: true. They will be removed in a future version:

v1 alias Use in v2
window.oziCore window.OZI
window.oziLoaddata window.oziLoadData
window.OziFrameworks OZI.integrations
window.zldActions(arr) OZI.modules.actions.run(arr)
window.oziValidateContainer(c) OZI.modules.validate.container(c)
zldParseBool / zldGenerateId / … OZI.helpers.*

8. Migration checklist

  • [ ] Include jQuery yourself if your app relied on the one bundled with ozi-ui (§2)
  • [ ] Move ozi:change consumers to addEventListener + e.detail (§3) — or enable the shim
  • [ ] Replace ozi-copy/ozi-paste with the Alpine recipes (§4)
  • [ ] (Livewire) consider mode A (data-ozi-livewire-native) to respect wire:model (§5)
  • [ ] (Tailwind) link the new overrides.css (§6)
  • [ ] Remove usages of legacy aliases (§7)
  • [ ] Run the app with oziConf({ core: { log: true } }) to see deprecation warnings

9. What does not change

  • data-ozi-* and data-zld-* attributes (declarative markup)
  • Public API: OZI.components.<name> and window.Ozi<Name>
  • oziConf({...}), @oziScripts / @oziStyles (Blade), OZI.ready()
  • ozi-loaddata: fetch, DOM target, collection, progress, actions — identical behavior