WAVECOREDocumentation · v1.0.0

Documentation

v1.0.0 · prefix av-

Ringkasan

WAVECORE adalah UI template HTML/CSS/JS statis untuk aplikasi admin: dashboard, data master, form, POS, dan faktur. Tanpa framework, tanpa jQuery, tanpa build step. Cukup satu CSS dan satu JS:

<link href="./assets/style.css" rel="stylesheet">
<script src="./assets/icons/feather.min.js"></script>
<script src="./assets/style.js"></script>

Semua class, id, dan variable CSS memakai prefix av- supaya tidak bentrok dengan CSS lain. Ikon memakai Feather (<i data-feather="home"></i>), diganti jadi SVG oleh feather.replace() di style.js.

Struktur Folder

PathIsi
index.htmlLanding page portofolio WAVECORE UI.
home.htmlBlank page: layout lengkap (topbar, sidebar, footer) tanpa konten. Titik awal halaman baru.
documentation.htmlHalaman ini.
login.htmlHalaman login (tanpa sidebar).
pages/*.htmlContoh halaman dan komponen (lihat bagian Halaman).
assets/style.cssSemua CSS: grid, utilitas, komponen, layout, dark mode, states, POS, invoice, documentation.
assets/style.jsSidebar accordion, ripple, toggle sidebar, dark mode, dropdown, tinggi table-freeze, modal.
assets/icons/feather.min.jsIkon Feather.
assets/images/Gambar (avatar).
assets/library/onvshark-*Library OnvShark: alert, toastify, tags-input, texteditor, select. Masing-masing punya .css, .js, .md.
data/*.jsonData dummy: datatable.json (tabel aset, pages/tables.html), dataselect-grouping.json (select grouping, pages/form.html).
doc/*.mdCatatan komponen (table-freeze.md).
DESIGN.md, PRODUCT.mdDesign system dan arah produk.

Halaman

MenuFileIsi
Pages
Dashboardpages/dashboard.htmlKPI, grafik SVG, ringkasan.
Tablespages/tables.htmlTabel aset dari data/datatable.json: freeze header, kolom tetap kiri/kanan, sort, pagination. Contoh tabel basic, stripe, small.
Modalpages/modal.htmlSemua ukuran modal (default, lg, xl, fullscreen), isi lorem.
Alertpages/alert.htmlKomponen .av-alert: default, outline, soft, left border + icon.
Badgepages/badge.htmlDefault, soft, pill, soft + pill.
Buttonpages/button.htmlDefault, outline, soft, rounded, size.
Formpages/form.htmlInput, select, checkbox, radio, switch, password, file, color, validasi.
Uploadpages/upload.htmlDrop zone .av-upload: drag & drop, validasi tipe/ukuran, tile dengan preview gambar.
Authlogin.htmlLogin.
OnvShark
Alertpages/onvshark-alert.htmlDialog konfirmasi onvsAlert().
Toastifypages/onvshark-toastify.htmlNotifikasi onvsToast().
Tags Inputpages/onvshark-tags.htmlOnvTagsInput.init(): bebas, master, creatable.
Text Editorpages/onvshark-texteditor.htmlOnvTextEditor.init() + sanitize().
Selectpages/onvshark-select.htmlONVSelect: search server, grouping lokal, multiple checklist.
Plugins & Addons
POSpages/pos.htmlKasir layar penuh: produk, keranjang, pembayaran (data dummy).
Invoicepages/invoice.htmlForm faktur penjualan layar penuh: customer, limit kredit, item, ringkasan (data dummy).

Membuat Halaman Baru

Salin home.html (di root) atau salah satu file di pages/ (di dalam folder pages/). File di pages/ memakai path ../assets/, link menu tanpa folder, dan Auth ke ../login.html. Menu sidebar ditulis di setiap file, jadi menu baru harus ditambahkan ke semua halaman.

<body data-topbar="dark" data-sidebar="brand" data-sidebar-size="lg">
    <div id="av-layout-wrapper">
        <header id="av-page-topbar">...</header>           <!-- logo, search, dark mode, burger -->
        <div class="av-vertical-menu">...</div>           <!-- profile + ul#av-side-menu -->
        <div class="av-main-content">
            <div class="av-page-content">
                <div class="av-container-fluid">
                    <div class="av-row"><div class="av-col-12">
                        <div class="av-page-title-box av-d-sm-flex av-align-items-center av-justify-content-between">
                            <h4 class="av-mb-sm-0 av-font-size-18">Judul</h4>
                        </div>
                    </div></div>
                    <!-- konten: av-row > av-col-* > av-card -->
                </div>
            </div>
            <footer class="av-footer">...</footer>
        </div>
    </div>
</body>
Atribut bodyNilai
data-layout-modelight / dark (diatur tombol bulan/matahari)
data-topbardark
data-sidebarbrand (biru) / dark
data-sidebar-sizelg (lebar) / sm (ikon saja)

Menu: <li class="av-menu-title"> untuk judul grup, <a class="av-has-arrow"> + <ul class="av-sub-menu"> untuk submenu.

Design Token

Sumber: :root di assets/style.css. Pakai variable, jangan hex langsung. Turunan: -soft (latar), -text (teks di atas soft), -border, -rgb (untuk rgba(var(--av-primary-rgb), .25)).

VariableFungsiBase-soft-text-borderLainnya-rgb
--av-themeIdentitas brand: sidebar, navbar, logo#1a2647#e8f0fb#012d61#3a6fc4--
--av-theme-secondaryPendamping brand#01397a#cddcf5#012d61---
--av-primaryAksi utama: simpan, submit#1a2647#e8f0fb#012d61--hover #3a6fc426, 38, 71
--av-secondaryAksi pendukung: batal, edit#74788d#eeeef3#4a4e6b--dark #4a4e6b116, 120, 141
--av-successPositif: selesai, baik#2ab57d#e1f5ee#0f6e56#5dcaa5-42, 181, 125
--av-dangerKritis: rusak, terlambat, hapus#fd625e#feecec#b93230#fda09e-253, 98, 94
--av-warningPerhatian: perlu cek, jatuh tempo#ffbf53#fff5e0#9b6a00#ffd48a-255, 191, 83
--av-infoNetral: terjadwal, notifikasi#4ba6ef#e6f1fb#185fa5#85b7eb-75, 166, 239
--av-lightPasif: inactive, disabled, border#e9e9ef#f5f5f8---233, 233, 239
--av-darkTeks / latar gelap#343a40----52, 58, 64
--av-accentLabel kategori, tag prioritas (maks 10% layar)#e83e8c#fde8f2#a31d62--dark #7a1449232, 62, 140
--av-whiteCard, modal, input#fff----255, 255, 255
--av-blackHanya -rgb (shadow, overlay)-----0, 0, 0
--av-body-colorTeks utama#495057----73, 80, 87
--av-body-bgLatar halaman#f8f8f6----248, 248, 246
VariableNilaiCatatan
--av-font-sans-serif"IBM Plex Sans", sans-serifFont utama
--av-font-monospaceSFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospaceKode, angka sejajar
--av-body-font-familyvar(--av-font-sans-serif)
--av-body-font-size0.875rem (14px)Minimum untuk konten utama
--av-body-font-weight400Label 500, heading 600
--av-body-line-height1.5Heading 1.2
--av-gradientlinear-gradient(180deg, rgba(255,255,255,.15), rgba(255,255,255,0))Kilap tombol. Jangan dipakai untuk header dan background
Radius4px · 6px · 8pxButton/input · modal · card
Spacing4 · 8 · 16 · 24 · 32px
Shadow-Datar saat diam, shadow hanya saat hover/fokus/modal

Komponen

KomponenClassContoh
Button av-btn + av-btn-{primary|secondary|success|info|warning|danger|light|dark}, av-btn-outline-*, av-btn-soft-*, av-btn-rounded, ukuran av-btn-xsm | av-btn-sm | av-btn-lg
Badge av-badge + av-bg-* atau av-badge-soft-*, av-rounded-pill Primary Soft Pill
Alert av-alert + av-alert-*, av-alert-outline-*, av-alert-soft-*, av-alert-border-* + av-alert-icon
Soft info
Card av-card > av-card-header (av-card-title) + av-card-body + av-card-footer. Toolbar tabel: av-card-header-toolbar > av-toolbar-left / av-toolbar-right Lihat card di halaman ini
Form av-form-control (+ av-form-control-sm), av-input-group + av-input-group-text, av-col-form-label-sm, av-form-check, av-form-switch. Validasi: is-valid / is-invalid + av-invalid-feedback
Grid av-container-fluid > av-row > av-col-{sm|md|lg|xl}-{1..12} Breakpoint seperti Bootstrap
Utilitas av-d-flex, av-align-items-center, av-justify-content-between, av-flex-wrap, av-gap-2, av-m*-*, av-p*-*, av-text-muted, av-text-sm-end, av-d-none, av-d-sm-block, av-font-size-* Prefix av- dari utilitas Bootstrap
Pagination av-pagination > av-page-item (av-active, av-disabled, av-ellipsis) > av-page-link; varian av-pagination-rounded
  • ‹
  • 1
  • 2
  • ›

Table

Tabel biasa: av-table + av-table-basic (border semua sisi), av-table-stripe (zebra + hover), av-table-sm. Bungkus dengan av-table-responsive untuk scroll horizontal.

Table freeze (header tetap, scroll di dalam):

<div class="av-table-responsive">
    <div class="av-table-freeze-wrapper" data-height="70vh">     <!-- default 70vh -->
        <div class="av-table-freeze-container">
            <table class="av-table-freeze" style="width:100%;">
                <thead><tr>
                    <th class="av-table-freeze-table-freeze-fixed-colomn-header av-th-sort" data-col="0">ID <span class="av-sort-icon">⇅</span></th>
                    ...
                    <th class="av-table-freeze-fixed-right">Action</th>
                </tr></thead>
                <tbody><tr>
                    <td class="av-table-freeze-fixed-colomn">AST-001</td>
                    ...
                    <td class="av-table-freeze-fixed-right">...</td>
                </tr></tbody>
            </table>
        </div>
    </div>
</div>
FiturCara
Tinggidata-height di .av-table-freeze-wrapper (CSS length apa pun). Dibaca style.js saat halaman dimuat.
Kolom tetap kiriav-table-freeze-table-freeze-fixed-colomn-header (th) + av-table-freeze-fixed-colomn (td)
Kolom tetap kananav-table-freeze-fixed-right di th dan setiap td
Sortav-th-sort + data-col + av-sort-icon. Logika sort ada di script halaman (pages/tables.html).
PaginationClient-side di pages/tables.html: avRenderTable(data, page), avRenderPagination(total), dropdown #avPerPage.
Teks panjangSel dipotong … di 200px, tampil penuh saat hover.

Modal

<button type="button" class="av-btn av-btn-primary" data-av-modal="av-modal_demo">Open</button>

<div class="av-modal" id="av-modal_demo">
    <div class="av-modal-dialog av-modal-lg">          <!-- kosong = default | av-modal-lg | av-modal-xl -->
        <div class="av-modal-header">
            <h5 class="av-modal-title">Title</h5>
            <button type="button" class="av-modal-close" data-av-dismiss>&#x2715;</button>
        </div>
        <div class="av-modal-body">...</div>
        <div class="av-modal-footer">
            <button type="button" class="av-btn av-btn-secondary" data-av-dismiss>Close</button>
        </div>
    </div>
</div>
  • Fullscreen: <div class="av-modal av-modal-fs-wrap"> + <div class="av-modal-dialog av-modal-fullscreen">.
  • data-av-modal="id" membuka, data-av-dismiss menutup. Klik overlay dan Esc juga menutup.
  • Dari JS: AvModal.open('id'), AvModal.close().

States

ClassFungsi
av-spinner, av-spinner-smSpinner. Tersembunyi, tampil di dalam button[data-loading] atau .av-loading-state.
av-loading-stateBaris/area "sedang memuat".
av-empty-state (+ -icon, -title, -text)Tampilan data kosong atau error.
av-modal-loadingOverlay loading di dalam modal.
is-valid, is-invalidStatus validasi di av-form-control.
av-sr-onlyHanya untuk screen reader. Live region: #av-live-region, #av-live-region-assertive.

style.js

BagianFungsi
Sidebar accordionBuka/tutup av-sub-menu; saat sidebar sm, submenu tampil sebagai popup dan menu tunggal sebagai tooltip.
RippleEfek gelombang saat tombol diklik.
Sidebar toggle#av-vertical-menu-btn (mobile) dan #av-sidebar-profile-toggle: ganti data-sidebar-size lg/sm.
Dark mode#av-mode-setting-btn: ganti data-layout-mode dan data-sidebar.
Dropdown user#av-page-header-user-dropdown + menu di sebelahnya (av-show).
Table freezeIsi tinggi dari data-height.
Featherfeather.replace() saat DOMContentLoaded. Panggil lagi setelah render HTML baru yang berisi data-feather.
Modaldata-av-modal, data-av-dismiss, overlay, Esc, window.AvModal.

Library OnvShark

Library mandiri (tanpa dependency) di assets/library/. Include CSS + JS hanya di halaman yang memakainya. Dokumentasi lengkap ada di file .md masing-masing dan di link docs.

LibraryFungsiFileDocs
AlertonvsAlert(icon, type, title, message, buttons)onvshark-alert/alert.css|jsdocs
ToastifyonvsToast(icon, type, message, subMessage, close, position)onvshark-toastify/toastify.css|jsdocs
Tags InputOnvTagsInput.init(id, { source, creatable, max, separator })onvshark-tags-input/tagsinput.css|jsdocs
Text EditorOnvTextEditor.init(id, { tools, color, size }), OnvTextEditor.sanitize(html)onvshark-texteditor/texteditor.css|jsdocs
Selectnew ONVSelect(select, { url | source, groupField, search, multiple })onvshark-select/select.css|jslihat bagian ONVSelect
onvsToast(true, 'success', 'Data saved', null, 3000, 'top-right');

onvsAlert(true, 'danger', 'Delete data?', 'This cannot be undone.', {
    confirm: { label: 'Delete', icon: 'trash', onClick: () => {} },
    cancel : { label: 'Cancel', icon: 'x' }
});

ONVSelect

Dropdown pengganti <select>: cari ke server, gulir per halaman, grup, checklist. Demo: pages/onvshark-select.html. Opsi lengkap: assets/library/onvshark-select/select.md.

1. Pasang CSS & JS
<link href="../assets/library/onvshark-select/select.css" rel="stylesheet">
<script src="../assets/library/onvshark-select/select.js"></script>
2. Buat <select> kosong
<!-- satu pilihan -->
<select id="aset" name="aset"></select>

<!-- banyak pilihan: tambah multiple + name[] -->
<select id="cabang" name="cabang[]" multiple></select>

Opsi tidak perlu ditulis. <select> asli disembunyikan dan tetap menyimpan nilai terpilih, jadi form submit jalan seperti biasa.

3. Jalankan ONVSelect
const aset = new ONVSelect('#aset', {
    url: '/api/assets',                 // data dari server
    valueField: 'id_aset',              // nilai yang disimpan
    formatText: (a) => `${a.id_aset} - ${a.nama_aset}`,
    paginationPath: 'meta.pagination',
    limit: 20,
    fullWidth: true,
    onChange: (value, text) => {},
});

aset.getValue();   // 'AST-001'
aset.reset();      // kosongkan
4. Data dari API

ONVSelect memanggil url saat dibuka, saat mengetik (jeda 400 ms), dan saat daftar digulir sampai bawah:

GET /api/assets?page=1&limit=20&search=forklift

Server membalas array di data dan info halaman di paginationPath:

{
  "data": [
    { "id_aset": "AST-001", "nama_aset": "Forklift Toyota 3T" }
  ],
  "meta": { "pagination": { "current_page": 1, "last_page": 5 } }
}

Contoh endpoint Laravel (Query Builder):

Route::get('/api/assets', function (Request $request) {
    $query = DB::table('assets')->whereNull('deleted_at');
    if ($request->search) {
        $query->where(fn ($q) => $q
            ->where('id_aset', 'like', "%{$request->search}%")
            ->orWhere('nama_aset', 'like', "%{$request->search}%"));
    }
    $page = $query->orderBy('id_aset')->paginate($request->limit ?? 20);

    return [
        'data' => $page->items(),
        'meta' => ['pagination' => [
            'current_page' => $page->currentPage(),
            'last_page'    => $page->lastPage(),
        ]],
    ];
});

Data sudah ada di halaman (tidak perlu server)? Pakai source: [...] sebagai pengganti url, lihat contoh Grouping.

Contoh

1. Search + Scroll Next Page (server)

<select id="avAssetPick" name="avAssetPick"></select>

const avAssetPick = new ONVSelect('#avAssetPick', {
    url: 'demo/assets',
    valueField: 'id_aset',
    formatText: (a) => `${a.id_aset} - ${a.nama_aset} (${a.kategori})`,
    paginationPath: 'meta.pagination',
    limit: 10,
    placeholder: '- Select asset -',
    searchPlaceholder: 'Type ID, name, category...',
    fullWidth: true,
    onChange: (value, text) => console.log(value, text),
});

2. Multiple Checklist (server)

<select id="avCustomerMulti" name="avCustomerMulti[]" multiple></select>

const avCustomerMulti = new ONVSelect('#avCustomerMulti', {
    url: 'demo/customers',
    valueField: 'id',
    formatText: (c) => `${c.code} - ${c.name}`,
    paginationPath: 'meta.pagination',
    limit: 20,
    placeholder: '- Select customers -',
    selectedText: '{n} customers selected',
    fullWidth: true,
    onChange: (values, texts) => console.log(values), // ['c1', 'c3']
});

// mode edit: isi pilihan tersimpan tanpa memanggil onChange
avCustomerMulti.selectOption('c1', 'C1C0000001 - Customer 1', false);

3. Grouping (data lokal)

<select id="avCityPlain" name="avCityPlain"></select>
<select id="avCitySearch" name="avCitySearch"></select>
<select id="avCityMulti" name="avCityMulti[]" multiple></select>

// [{ "id": "bdg", "name": "Bandung", "group": "Jawa Barat" }, ...]
fetch('../data/dataselect-grouping.json')
    .then(res => res.json())
    .then(json => {
        const base = { source: json.data, valueField: 'id', textField: 'name', groupField: 'group', fullWidth: true };

        new ONVSelect('#avCityPlain',  { ...base, search: false });     // tanpa search
        new ONVSelect('#avCitySearch', base);                           // search
        new ONVSelect('#avCityMulti',  { ...base, multiple: true });    // search + checklist
    });

POS & Invoice

  • Halaman layar penuh tanpa sidebar. Body memakai class="av-pos-page" atau class="av-inv-page"; aturan body di CSS hanya berlaku untuk class itu.
  • CSS ada di style.css bagian POS (av-pos-*) dan SALES INVOICE (av-inv-*).
  • Data dummy di objek DATA di script halaman. Untuk project nyata, ganti dengan panggilan API (toko/gudang, customer, metode bayar, kategori, produk dengan satuan dan harga retail/wholesale).
  • POS: F2 cari, F9 bayar, Esc tutup pembayaran. Enter di kolom cari dengan barcode/SKU persis langsung masuk keranjang. Stok dihitung dalam satuan dasar.
  • Invoice: cek limit kredit (piutang + sisa tagihan), uang muka, termin, Save as Draft, Post (mengunci form). Print memanggil window.print().
  • Mode gelap disimpan di localStorage key av-mode.

Aturan

  • Semua class dan id baru memakai prefix av- (class halaman khusus: av-pos-, av-inv-, av-doc-). Library OnvShark memakai prefix sendiri (onvs-).
  • CSS ditulis di assets/style.css, bukan <style> di halaman. Kalau tetap perlu inline untuk satu halaman, beri komentar alasannya.
  • Warna lewat variable --av-*. Tanpa gradient untuk header dan background.
  • Teks yang ditampilkan dari data selalu di-escape (avEscapeHtml / esc). HTML dari editor ditampilkan lewat OnvTextEditor.sanitize().
  • Setiap elemen interaktif punya label (aria-label, title, atau teks) dan bisa dipakai dengan keyboard.
  • Menu sidebar ditulis manual di setiap halaman; menambah menu = ubah semua file.