@skyworx/dynamic-ui

Vue 3 Dynamic UI Component Library - Compatible with Vue, React, Angular, and any other framework via Web Components
2,322
Install
npm install @skyworx/dynamic-ui
Latest Version:1.0.28
Vue:3
License:MIT
Last Updated:Oct 22, 2026
Links: npm
Maintainer: rzkmufid

@gemafajarramadhan/dynamic-ui

Library Vue 3 component yang bisa digunakan di berbagai framework (Vue, React, Angular, Svelte, dll.) untuk membuat Dynamic Form dan DataTable otomatis dari JSON schema, sekaligus menyediakan komponen UI yang kaya fitur.


Daftar Isi


Fitur

  • 🧩 Web Components – Dapat digunakan di framework apapun (React, Vue, Angular, Svelte, dll.) atau di HTML biasa.
  • 📋 AutoLayoutForm – Generate form otomatis dari JSON schema dengan validasi dan konfigurasi fleksibel.
  • 📊 AutoLayoutDatatable – Generate tabel dengan pagination, sorting, filtering, dan action button dari JSON.
  • 🔌 Logic Registry – Daftarkan custom business logic dari aplikasi host tanpa perlu memodifikasi library.
  • 🎨 Style Bundled – CSS sudah ter-bundle, cukup import satu file style.
  • 🌐 i18n Ready – Mendukung internasionalisasi (vue-i18n).
  • 🔐 API Config – Base URL dan auth token diambil dari host project, bukan dari env package ini.

Instalasi

# npm
npm install @gemafajarramadhan/dynamic-ui

# yarn
yarn add @gemafajarramadhan/dynamic-ui

# pnpm
pnpm add @gemafajarramadhan/dynamic-ui

⚡ Konfigurasi API (WAJIB)

Penting: Komponen seperti DCodeAutoComplete melakukan HTTP request ke API. Base URL dan auth token diambil dari host project, bukan dari .env package ini. Anda wajib mengkonfigurasi ini di aplikasi host sebelum menggunakan komponen.

Library menggunakan sistem konfigurasi terpusat via setApiConfig() (atau via Vue Plugin). Berikut urutan prioritas resolusi base URL:

Prioritas Sumber
1️⃣ Tertinggi apiBaseUrl via plugin / setApiConfig()
2️⃣ window.__MICRO_API_BASE_URL__ (global)
3️⃣ import.meta.env.VITE_API_BASE_URL (env host)
4️⃣ Terendah '' (same-origin fallback)

Konfigurasi API di Vue 3

Cara paling direkomendasikan: lewatkan config saat app.use(), atau gunakan setApiConfig secara langsung. Hal ini sangat penting agar request API dari komponen (seperti DCodeAutoComplete) mem-bypass native fetch bawaan dan menggunakan instance HTTP (seperti Axios) milik project host Anda. Dengan begitu, request otomatis melewati interceptor (untuk inject token) dan mengarah ke base URL yang tepat saat di production.

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import DynamicUI, { setApiConfig } from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

// Contoh import instance Axios / useApi dari host project Anda
import axiosInstance from './plugins/axios' 
// import { useApi } from './composables/useApi' 

const app = createApp(App)

// OPSI 1: Konfigurasi memanggil fungsi (Bisa dipanggil di luar instalasi plugin)
setApiConfig({
  // WAJIB: Bypass fetch bawaan, gunakan axios dari project host
  apiClient: async (url, method, data) => {
    return axiosInstance({ url, method, data })
    /* ATAU MENGGUNAKAN useApi bawaan project host
    const { apiUrl } = useApi()
    return apiUrl(url, method || 'GET', data, { disableGlobalLoading: true })
    */
  },
  
  // Base URL API dari env HOST project
  apiBaseUrl: import.meta.env.VITE_API_BASE_URL,
  
  // (Opsional jika apiClient sudah memiliki interceptor token)
  getAuthToken: () => localStorage.getItem('access_token'),
})

// OPSI 2: Lewatkan pada saat app.use (Berlaku jika tidak menggunakan setApiConfig di atas)
app.use(DynamicUI, {
  apiClient: async (url, method, data) => axiosInstance({ url, method, data }),
  apiBaseUrl: import.meta.env.VITE_API_BASE_URL,
})

app.mount('#app')

File .env di host project Anda:

VITE_API_BASE_URL=https://api.example.com

Konfigurasi API di Nuxt 3

// plugins/dynamic-ui.ts
import DynamicUI from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()

  nuxtApp.vueApp.use(DynamicUI, {
    // Ambil dari runtimeConfig Nuxt
    apiBaseUrl: config.public.apiBaseUrl,

    getAuthToken: () => useCookie('access_token').value ?? null,

    onUnauthorized: () => {
      navigateTo('/login')
    },
  })
})

File nuxt.config.ts di host project:

export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      apiBaseUrl: process.env.VITE_API_BASE_URL, // set di .env
    },
  },
})

Konfigurasi API di React / Next.js

Gunakan fungsi setApiConfig() secara langsung (tidak perlu Vue plugin).

// src/lib/dynamic-ui-config.ts (jalankan sebelum render app)
import { setApiConfig } from '@gemafajarramadhan/dynamic-ui'

setApiConfig({
  apiBaseUrl: process.env.NEXT_PUBLIC_API_BASE_URL,

  getAuthToken: () => localStorage.getItem('access_token'),

  onUnauthorized: () => {
    window.location.href = '/login'
  },
})

Panggil di entry point:

// app/layout.tsx atau _app.tsx
'use client'
import '@/lib/dynamic-ui-config' // ← jalankan konfigurasi lebih dulu
import '@gemafajarramadhan/dynamic-ui/style.css'

File .env.local di host project Next.js:

NEXT_PUBLIC_API_BASE_URL=https://api.example.com

Konfigurasi API di Angular

// main.ts
import { setApiConfig } from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

// Konfigurasi sebelum bootstrap
setApiConfig({
  apiBaseUrl: (window as any).__env?.API_BASE_URL || 'https://api.example.com',

  getAuthToken: () => localStorage.getItem('access_token'),

  onUnauthorized: () => {
    window.location.href = '/login'
  },
})

platformBrowserDynamic().bootstrapModule(AppModule)

Konfigurasi API via Window Global

Alternatif tanpa import fungsi – set sebelum library dimuat:

<script>
  // Set sebelum import library
  window.__MICRO_API_BASE_URL__ = 'https://api.example.com'
  window.__MICRO_AUTH_TOKEN__ = 'your-auth-token' // atau ambil dari cookie/localStorage
</script>
<script type="module" src="./main.js"></script>

Komponen Base (Vue Components)

Selain Web Components, library ini juga menyediakan 27 komponen Vue yang bisa digunakan langsung di aplikasi Vue 3, Nuxt 3, dan framework apapun yang mendukung Vue.

Daftar Komponen

Komponen Deskripsi
DCodeTextField Input text dengan validasi, text case, filter karakter
DCodeTextarea Textarea dengan auto-resize dan validasi
DCodeButton Tombol dengan variant, warna kustom, tooltip, dan animasi
DCodeCard Kartu container dengan styling bawaan
DCodeCheckbox Checkbox dengan label
DCodeSwitch Toggle switch
DCodeRadioCustom Radio button dengan opsi kustom
DCodeLabel Label untuk form
DCodeAutoComplete Input autocomplete dengan pencarian dan fetch API otomatis
DCodeMultiSelect Dropdown multi-pilihan
DCodeDatePicker Date picker kalender
DCodeCurrencyField Input nilai mata uang
DCodeOtpInput Input OTP digit-per-digit
DCodeFileField Input file dengan preview
DCodeImageField Input gambar dengan preview
DCodeDropzone Drag-and-drop upload area
DCodeUploadFile Upload file terintegrasi
DCodeFileResult Tampilan hasil file yang diupload
DCodeImageResult Tampilan hasil gambar yang diupload
DCodeImageCropperDialog Dialog crop gambar
DCodeDialog Modal dialog
DCodeDialogCloseBtn Tombol tutup untuk dialog
DCodeIconDropdown Dropdown ikon
DCodeProgressBar Progress bar loading
DCodeTimelineWithIcons Timeline dengan ikon
DCodeWizard Wizard / stepper multi-langkah

Penggunaan di Vue 3 (Import Per Komponen)

Cara paling ringan: import hanya komponen yang diperlukan.

<!-- MyForm.vue -->
<script setup>
import { DCodeTextField, DCodeButton } from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'
</script>

<template>
  <form>
    <DCodeTextField
      v-model="form.name"
      label="Nama Lengkap"
      placeholder="Masukkan nama..."
      :clearable="true"
    />

    <DCodeTextField
      v-model="form.email"
      label="Email"
      valueType="email"
      placeholder="nama@email.com"
    />

    <DCodeButton
      text="Simpan"
      variant="default"
      bgColor="primary"
      type="submit"
    />
  </form>
</template>

<script setup>
import { reactive } from 'vue'

const form = reactive({ name: '', email: '' })
</script>

Penggunaan di Vue 3 (Global Plugin)

Install semua komponen sekaligus agar bisa digunakan di mana saja tanpa import per file.

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import DynamicUI from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

const app = createApp(App)

app.use(DynamicUI, {
  apiBaseUrl: import.meta.env.VITE_API_BASE_URL,
  getAuthToken: () => localStorage.getItem('access_token'),
})

app.mount('#app')

Setelah itu, semua komponen bisa digunakan tanpa import di setiap file:

<!-- Tidak perlu import lagi! -->
<template>
  <DCodeTextField v-model="name" label="Nama" />
  <DCodeButton text="Submit" />
  <DCodeDatePicker v-model="date" label="Tanggal" />
</template>

Penggunaan di Nuxt 3 (Vue Components)

Buat plugin Nuxt untuk mendaftarkan semua komponen secara global:

// plugins/dynamic-ui.ts
import DynamicUI from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()

  nuxtApp.vueApp.use(DynamicUI, {
    apiBaseUrl: config.public.apiBaseUrl,
    getAuthToken: () => useCookie('access_token').value ?? null,
  })
})

Kemudian gunakan di page atau component manapun:

<!-- pages/index.vue -->
<template>
  <div>
    <DCodeTextField v-model="search" label="Pencarian" :clearable="true" />
    <DCodeButton text="Cari" variant="default" />
  </div>
</template>

DCodeDataTable – Tabel Dinamis

DCodeDataTable adalah komponen tabel dinamis yang mendukung pagination server-side, sorting, filtering, action buttons, dan badge kondisional — seluruhnya dikonfigurasi via JSON schema.

Komponen ini dirancang sebagai micro-frontend: consumer bertanggung jawab penuh atas HTTP client, auth token, dan sistem permission.


Props Utama

Prop Tipe Default Deskripsi
table-config Object — Config JSON (kolom, filter, action, pagination)
fetch-handler (params) => Promise<any> — [MFE] Consumer kontrol penuh fetch. Component tidak hit endpoint sama sekali.
http-client ({ url, method, data }) => Promise<any> — [MFE] Axios instance consumer untuk action tombol row/header (DELETE, PATCH, dll.)
action-access-checker (path, action) => boolean () => true [MFE] Fungsi RBAC dari consumer. Default: semua action allow.
ignore-permissions boolean false Matikan filter permission sepenuhnya
data any[] [] Data statis (tanpa API); pakai jika consumer sudah fetch di luar
loading boolean false Loading state untuk mode data statis
columns TableColumn[] [] Kolom manual (tanpa table-config)
per-page number 10 Jumlah baris per halaman awal
show-pagination boolean true Tampilkan pagination
has-actions boolean false Tampilkan kolom aksi (untuk mode kolom manual)
api-url string — URL endpoint langsung (tanpa table-config)
api-method string 'POST' HTTP method untuk apiUrl
api-params object {} Parameter tetap yang selalu disertakan di setiap request

Events

Event Payload Deskripsi
@action { action, row, selection? } Dipanggil saat tombol action diklik
@update:selection any[] Dipanggil saat checkbox selection berubah
@update:page number Dipanggil saat halaman berganti

Pola 1 — fetchHandler ⭐ Disarankan untuk MFE

Consumer kontrol penuh HTTP. Component tidak hit endpoint sama sekali — cocok untuk menghindari error 401.

<template>
  <DCodeDataTable
    :table-config="tableConfig"
    :fetch-handler="fetchUsers"
    :http-client="httpClient"
    :action-access-checker="checkPermission"
    @action="handleAction"
  />
</template>

<script setup lang="ts">
import { DCodeDataTable } from '@gemafajarramadhan/dynamic-ui'
import axios from '@/lib/axios' // axios instance consumer — interceptor token sudah terpasang

// 1. fetchHandler: dipanggil tiap page/filter/sort/search change
//    params: { page, perPage, keyword?, ...filters }
const fetchUsers = async (params: Record<string, any>) => {
  const { data } = await axios.post('/api/users/list', params)
  return data
}

// 2. httpClient: untuk call dari tombol action row (delete, toggle, dll.)
const httpClient = async ({ url, method, data }: any) => {
  const res = await axios({ url, method, data })
  return res.data
}

// 3. Permission checker dari sistem RBAC consumer
const checkPermission = (path: string, action: string): boolean => {
  return useAuthStore().can(`${path}:${action}`)
}

// 4. Handle action event dari tabel
const handleAction = ({ action, row, selection }: any) => {
  if (action === 'edit')   router.push(`/users/${row.id}/edit`)
  if (action === 'delete') deleteUser(row.id)
  if (action === 'export') exportSelected(selection)
}

// 5. Konfigurasi tabel via JSON schema
const tableConfig = {
  headers: [
    { key: 'no',    labelID: 'No',    labelEN: 'No',    align: 'CENTER' },
    { key: 'name',  labelID: 'Nama',  labelEN: 'Name',  align: 'LEFT' },
    { key: 'email', labelID: 'Email', labelEN: 'Email', align: 'LEFT' },
    {
      key: 'status',
      labelID: 'Status', labelEN: 'Status',
      custom: {
        type: 'BADGE',
        conditionBadge: [
          { value: 'active',   labelID: 'Aktif',    labelEN: 'Active',   color: 'success' },
          { value: 'inactive', labelID: 'Nonaktif', labelEN: 'Inactive', color: 'danger' },
        ],
      },
    },
    {
      key: 'action',
      labelID: 'Aksi', labelEN: 'Actions',
      custom: {
        type: 'ACTION',
        actionCustom: [
          {
            permission: 'EDIT',
            propsActionCustom: {
              labelID: 'Edit', labelEN: 'Edit',
              icon: 'SquarePen', actionType: 'edit', bgColor: 'primary',
            },
          },
          {
            permission: 'DELETE',
            requireConfirm: true,
            propCustomMessage: {
              titleID: 'Hapus Data', titleEN: 'Delete',
              messageID: 'Yakin ingin menghapus?', messageEN: 'Are you sure?',
            },
            propsActionCustom: {
              labelID: 'Hapus', labelEN: 'Delete',
              icon: 'Trash2', actionType: 'delete', bgColor: 'red',
            },
          },
        ],
      },
    },
  ],
  filters: [
    {
      seq: 1, model: 'name', labelID: 'Nama', labelEN: 'Name', type: 'TEXT',
    },
    {
      seq: 2, model: 'status', labelID: 'Status', labelEN: 'Status', type: 'SELECT',
      propsFilters: {
        items: [
          { value: 'active',   label: 'Active' },
          { value: 'inactive', label: 'Inactive' },
        ],
      },
    },
  ],
  actionGlobal: [
    {
      permission: 'CREATE',
      propsActionGlobal: {
        labelID: 'Tambah', labelEN: 'Add New',
        icon: 'Plus', variant: 'default',
        route: '/users/create',
      },
    },
  ],
  pagination: true,
}
</script>

Pola 2 — Static Data (consumer fetch di luar)

Pakai ketika consumer sudah fetch data sendiri dan ingin sekadar menampilkan tabel.

<template>
  <DCodeDataTable
    :columns="columns"
    :data="users"
    :loading="isLoading"
    has-actions
    ignore-permissions
    @action="handleAction"
  />
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import axios from '@/lib/axios'

const users = ref([])
const isLoading = ref(false)

onMounted(async () => {
  isLoading.value = true
  const { data } = await axios.get('/api/users')
  users.value = data.data
  isLoading.value = false
})

const columns = [
  { key: 'no',    label: 'No',    align: 'center' },
  { key: 'name',  label: 'Nama',  align: 'left' },
  { key: 'email', label: 'Email', align: 'left' },
]
</script>

Pola 3 — Realtime Socket (consumer kelola socket)

Consumer connect ke WebSocket sendiri, push data ke tabel melalui :data.

<template>
  <DCodeDataTable :columns="columns" :data="realtimeData" ignore-permissions />
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { io } from 'socket.io-client'

const realtimeData = ref([])
let socket: any = null

onMounted(() => {
  socket = io('wss://your-server.com', {
    auth: { token: localStorage.getItem('token') }
  })
  socket.emit('dashboard:list', { page: 1, perPage: 10 })
  socket.on('dashboard:result', (res: any) => {
    realtimeData.value = res.data ?? res
  })
})

onUnmounted(() => socket?.disconnect())
</script>

Format Response yang Didukung

Component secara otomatis mendeteksi berbagai format response API:

{ "data": [], "totalData": 100 }    ✅ (disarankan)
{ "data": [], "maxPage": 10 }       ✅
{ "items": [], "total": 100 }       ✅
{ "list": [], "total": 100 }        ✅
{ "rows": [], "count": 100 }        ✅
{ "result": [], "total": 100 }      ✅
[]                                  ✅ (array langsung)

Expose Methods (via ref)

const tableRef = ref()

tableRef.value.fetchData()      // Refresh data (panggil ulang fetchHandler)
tableRef.value.handleRefresh()  // Reset filter & refresh
tableRef.value.resetFilters()   // Reset nilai filter saja (tanpa fetch)
 // Reactive nilai filter saat ini
<DCodeDataTable ref="tableRef" :fetch-handler="fetchUsers" ... />

DCodeAutoComplete – Komponen dengan API

DCodeAutoComplete adalah komponen autocomplete yang bisa langsung fetch data dari API menggunakan konfigurasi yang sudah di-set di host project.

⚠️ Pastikan sudah mengatur Konfigurasi API sebelum menggunakan fitur endpoint.

Perilaku Default

  • Setiap fetch otomatis mengirimkan page=1 dan perPage=100 sebagai query parameter default.
  • Nilai ini bisa di-override via prop apiParams.

Props Utama

Prop Tipe Default Deskripsi
modelValue any — Value terpilih (v-model)
endpoint.apiUrl string — URL endpoint API (path atau full URL)
endpoint.apiMethod string 'GET' HTTP method
apiParams object {} Query params tambahan (di-merge & bisa override default)
itemTitle string|Function 'name' Field nama tampilan dari tiap item
itemValue string|Function 'id' Field nilai dari tiap item
returnObject boolean false Jika true, v-model menyimpan objek penuh, bukan hanya id
clearable boolean false Tampilkan tombol hapus pilihan
disabled boolean false Nonaktifkan komponen
autoFetchOnOpen boolean true Fetch otomatis saat dropdown dibuka pertama kali
autoFetchOnMount boolean false Fetch otomatis saat komponen pertama kali di-mount

Contoh Penggunaan

Fetch dari API dengan URL langsung
<template>
  <DCodeAutoComplete
    v-model="selectedId"
    label="Pilih Departemen"
    :endpoint="{ apiUrl: '/api/master/departemen', apiMethod: 'GET' }"
    item-title="nama"
    item-value="id"
    clearable
  />
</template>

Request yang dikirim: GET /api/master/departemen?page=1&perPage=100 Base URL diambil otomatis dari konfigurasi host project (apiBaseUrl).

Dengan Parameter Tambahan
<template>
  <DCodeAutoComplete
    v-model="selectedId"
    label="Pilih Jabatan"
    :endpoint="{ apiUrl: '/api/master/jabatan' }"
    :apiParams="{ idDepartemen: selectedDeptId, isActive: true }"
    item-title="namaJabatan"
    item-value="idJabatan"
  />
</template>

Request yang dikirim: GET /api/master/jabatan?page=1&perPage=100&idDepartemen=5&isActive=true

Override Default Pagination
<template>
  <!-- Ganti perPage menjadi 50 -->
  <DCodeAutoComplete
    v-model="selected"
    label="Pilih Data"
    :endpoint="{ apiUrl: '/api/data' }"
    :apiParams="{ perPage: 50 }"
    item-title="nama"
    item-value="id"
  />
</template>
Dengan Data Statis (tanpa API)
<template>
  <DCodeAutoComplete
    v-model="selected"
    label="Pilih Status"
    :items="[
      { id: 1, name: 'Aktif' },
      { id: 0, name: 'Nonaktif' },
    ]"
    item-title="name"
    item-value="id"
  />
</template>
Return Object Penuh
<script setup>
import { ref } from 'vue'

const selectedUser = ref(null) // akan berisi { id: 1, name: 'John', email: '...' }
</script>

<template>
  <DCodeAutoComplete
    v-model="selectedUser"
    label="Pilih User"
    :endpoint="{ apiUrl: '/api/users' }"
    item-title="name"
    item-value="id"
    return-object
  />
</template>

Format Response API yang Diharapkan

{
  "status": true,
  "data": {
    "data": [
      { "id": 1, "name": "Item A" },
      { "id": 2, "name": "Item B" }
    ]
  }
}

Atau format flat:

{
  "data": [
    { "id": 1, "name": "Item A" }
  ]
}

Atau array langsung:

[
  { "id": 1, "name": "Item A" }
]

Web Components (Framework Agnostic)

Untuk digunakan di React, Angular, Svelte, atau framework lainnya, gunakan mode Web Components.


API Referensi Web Components

Web Components

Setelah library di-import, dua custom element berikut akan terdaftar secara global:

Custom Element Deskripsi
<micro-dynamic-form> Render form dinamis berdasarkan JSON config config prop.
<micro-dynamic-datatable> Render tabel dinamis berdasarkan JSON config config prop.
<micro-data-table> Render Data Table base component secara independen. Menerima props seperti columns, data, apiUrl.
Properties (set via JavaScript/DOM)

Karena Web Components menerima objek JavaScript (bukan string HTML attribute), set properti ini secara imperatif via DOM atau ref:

Property Tipe Deskripsi
config Object Konfigurasi form / tabel dalam bentuk JSON.
Events
Event event.detail Deskripsi
submit Object (payload) Dipanggil saat form di-submit.
action Object (payload) Dipanggil saat action button datatable diklik.

Logic Registry API

Digunakan untuk menyuntikkan business logic dari aplikasi host ke dalam komponen.

import {
  registerLogic,
  unregisterLogic,
  getLogicModule
} from '@gemafajarramadhan/dynamic-ui'
Fungsi Deskripsi
registerLogic(code, module) Daftarkan logic module untuk layout code tertentu.
unregisterLogic(code) Hapus logic module dari registry.
getLogicModule(code) Ambil logic module berdasarkan layout code.

Penggunaan di Berbagai Framework


1. Vanilla HTML / JavaScript

Cara paling sederhana. Import library dari bundler, lalu gunakan custom element di HTML.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Dynamic UI</title>
    <!-- Import style -->
    <link rel="stylesheet" href="./node_modules/@gemafajarramadhan/dynamic-ui/dist/style.css" />
  </head>
  <body>

    <!-- Dynamic Form -->
    <micro-dynamic-form id="myForm"></micro-dynamic-form>

    <!-- Dynamic DataTable (AutoLayout version) -->
    <micro-dynamic-datatable id="myAutoTable"></micro-dynamic-datatable>

    <!-- Base DataTable (Stand-alone version) -->
    <micro-data-table id="myBaseTable"></micro-data-table>

    <!-- Import library (registrasi Web Components otomatis) -->
    <script type="module">
      import '@gemafajarramadhan/dynamic-ui'
      import { setApiConfig } from '@gemafajarramadhan/dynamic-ui'

      // ✅ Konfigurasi API dari host project
      setApiConfig({
        apiBaseUrl: 'https://api.example.com',
        getAuthToken: () => localStorage.getItem('access_token'),
      })

      // --- Setup Form ---
      const form = document.querySelector('#myForm')
      form.config = {
        title: 'Registrasi User',
        sections: [
          {
            title: 'Info Pribadi',
            fields: [
              {
                key: 'name',
                label: 'Nama Lengkap',
                model: 'fullName',
                component: 'DCodeTextField',
              },
              {
                key: 'email',
                label: 'Email',
                model: 'email',
                component: 'DCodeTextField',
                props: { type: 'email' },
              },
            ],
          },
        ],
        actions: [{ key: 'submit', label: 'Daftar', component: 'DCodeButton' }],
      }

      form.addEventListener('submit', (e) => {
        
      })

      // --- Setup DataTable ---
      const autoTable = document.querySelector('#myAutoTable')
      autoTable.config = {
        titleID: 'Daftar User',
        headers: [
          { text: 'Nama', value: 'name' },
          { text: 'Email', value: 'email' },
        ],
      }

      autoTable.addEventListener('action', (e) => {
        
      })

      // --- Setup Base MicroDataTable ---
      const baseTable = document.querySelector('#myBaseTable')
      // Untuk MicroDataTable (base component), Anda bisa mengirimkan prop langsung:
      baseTable.columns = [
        { key: 'name', label: 'Nama', sortable: true },
        { key: 'email', label: 'Email' }
      ]
      // Memberikan data secara statis
      baseTable.data = [
        { name: 'John Doe', email: 'john@example.com' },
        { name: 'Jane Smith', email: 'jane@example.com' }
      ]
      
      // Atau bisa diset melalui API:
      // baseTable.apiUrl = '/api/users'
      // baseTable.apiMethod = 'GET'
    </script>

  </body>
</html>

2. Vue 3 (Web Components)

Di Vue 3, Web Components bisa dipakai langsung seperti HTML element biasa. Gunakan ref untuk set properti objek.

Konfigurasi Vite

Agar Vue tidak menganggap micro-dynamic-* sebagai Vue component yang belum terdaftar, tambahkan isCustomElement di vite.config.js:

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // Semua element yang dimulai dengan 'micro-' dianggap custom element
          isCustomElement: (tag) => tag.startsWith('micro-'),
        },
      },
    }),
  ],
})
Entry Point (main.js)
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import DynamicUI from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

const app = createApp(App)

// ✅ Konfigurasi API dari env host project
app.use(DynamicUI, {
  apiBaseUrl: import.meta.env.VITE_API_BASE_URL,
  getAuthToken: () => localStorage.getItem('access_token'),
  onUnauthorized: () => router.push('/login'),
})

app.mount('#app')
Komponen Vue (MyForm.vue)
<script setup>
import { ref, onMounted } from 'vue'

const formRef = ref(null)

const formConfig = {
  title: 'Form User',
  sections: [
    {
      fields: [
        {
          key: 'username',
          label: 'Username',
          model: 'username',
          component: 'DCodeTextField',
        },
      ],
    },
  ],
  actions: [{ key: 'save', label: 'Simpan', component: 'DCodeButton' }],
}

onMounted(() => {
  if (formRef.value) {
    // Set config via DOM property (bukan attribute)
    formRef.value.config = formConfig

    formRef.value.addEventListener('submit', (e) => {
      
    })
  }
})
</script>

<template>
  <micro-dynamic-form ref="formRef" />
</template>

3. React

Di React, gunakan useRef dan useEffect untuk set properti DOM secara imperatif karena React tidak meneruskan objek langsung ke Web Component attributes.

Entry Point (main.jsx)
// main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { setApiConfig } from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

// ✅ Konfigurasi API dari env host project
setApiConfig({
  apiBaseUrl: import.meta.env.VITE_API_BASE_URL,
  getAuthToken: () => localStorage.getItem('access_token'),
  onUnauthorized: () => { window.location.href = '/login' },
})

ReactDOM.createRoot(document.getElementById('root')).render(<App />)
Dynamic Form Component
// components/DynamicForm.jsx
import React, { useEffect, useRef } from 'react'

const formConfig = {
  title: 'Form via React',
  sections: [
    {
      fields: [
        {
          key: 'username',
          label: 'Username',
          model: 'username',
          component: 'DCodeTextField',
        },
        {
          key: 'email',
          label: 'Email',
          model: 'email',
          component: 'DCodeTextField',
          props: { type: 'email' },
        },
      ],
    },
  ],
  actions: [{ key: 'submit', label: 'Kirim', component: 'DCodeButton' }],
}

export default function DynamicForm() {
  const formRef = useRef(null)

  useEffect(() => {
    const el = formRef.current
    if (!el) return

    // Set config sebagai DOM property
    el.config = formConfig

    const handleSubmit = (event) => {
      
    }

    el.addEventListener('submit', handleSubmit)

    // Cleanup listener saat unmount
    return () => {
      el.removeEventListener('submit', handleSubmit)
    }
  }, [])

  return <micro-dynamic-form ref={formRef}></micro-dynamic-form>
}
Dynamic DataTable Component
// components/DynamicTable.jsx
import React, { useEffect, useRef } from 'react'

const tableConfig = {
  titleID: 'Daftar User',
  headers: [
    { text: 'Nama', value: 'name' },
    { text: 'Email', value: 'email' },
    { text: 'Status', value: 'status' },
  ],
}

export default function DynamicTable() {
  const tableRef = useRef(null)

  useEffect(() => {
    const el = tableRef.current
    if (!el) return

    el.config = tableConfig

    const handleAction = (event) => {
      
    }

    el.addEventListener('action', handleAction)

    return () => {
      el.removeEventListener('action', handleAction)
    }
  }, [])

  return <micro-dynamic-datatable ref={tableRef}></micro-dynamic-datatable>
}
TypeScript + React

Tambahkan deklarasi tipe agar JSX tidak mengeluh tentang custom element:

// src/types/dynamic-ui.d.ts
import React from 'react'

declare global {
  namespace JSX {
    interface IntrinsicElements {
      'micro-dynamic-form': React.DetailedHTMLProps<
        React.HTMLAttributes<HTMLElement>,
        HTMLElement
      >
      'micro-dynamic-datatable': React.DetailedHTMLProps<
        React.HTMLAttributes<HTMLElement>,
        HTMLElement
      >
    }
  }
}

4. Next.js (App Router)

Di Next.js dengan App Router, Web Components harus di-import di sisi klien ('use client').

Setup Konfigurasi API
// lib/dynamic-ui-config.ts
import { setApiConfig } from '@gemafajarramadhan/dynamic-ui'

setApiConfig({
  apiBaseUrl: process.env.NEXT_PUBLIC_API_BASE_URL!,
  getAuthToken: () => localStorage.getItem('access_token'),
  onUnauthorized: () => { window.location.href = '/login' },
})
Setup di Layout atau Client Component
// components/DynamicForm.tsx
'use client'

import { useEffect, useRef } from 'react'
import '@/lib/dynamic-ui-config'
import('@gemafajarramadhan/dynamic-ui')
import '@gemafajarramadhan/dynamic-ui/style.css'

const formConfig = {
  title: 'Form Next.js',
  sections: [
    {
      fields: [
        {
          key: 'name',
          label: 'Nama',
          model: 'name',
          component: 'DCodeTextField',
        },
      ],
    },
  ],
  actions: [{ key: 'submit', label: 'Submit', component: 'DCodeButton' }],
}

export default function DynamicFormClient() {
  const formRef = useRef<HTMLElement>(null)

  useEffect(() => {
    const el = formRef.current
    if (!el) return

    ;(el as any).config = formConfig

    const handleSubmit = (event: Event) => {
      const customEvent = event as CustomEvent
      
    }

    el.addEventListener('submit', handleSubmit)
    return () => el.removeEventListener('submit', handleSubmit)
  }, [])

  return (
    // @ts-ignore – custom element tidak dikenal TypeScript secara default
    <micro-dynamic-form ref={formRef}></micro-dynamic-form>
  )
}
Penggunaan di Page
// app/page.tsx
import DynamicFormClient from '@/components/DynamicForm'

export default function HomePage() {
  return (
    <main>
      <h1>Dynamic Form</h1>
      <DynamicFormClient />
    </main>
  )
}

Catatan: Gunakan dynamic() dari next/dynamic dengan { ssr: false } jika terjadi error SSR.

import dynamic from 'next/dynamic'
const DynamicFormClient = dynamic(() => import('@/components/DynamicForm'), { ssr: false })

File .env.local di host project:

NEXT_PUBLIC_API_BASE_URL=https://api.example.com

5. Angular

Di Angular, tambahkan CUSTOM_ELEMENTS_SCHEMA agar Angular tidak melempar error pada custom element yang tidak dikenali.

app.module.ts (Module-based)
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { AppComponent } from './app.component'

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  schemas: [CUSTOM_ELEMENTS_SCHEMA], // <-- Wajib ditambahkan
  bootstrap: [AppComponent],
})
export class AppModule {}
main.ts
// main.ts
import { setApiConfig } from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

// ✅ Konfigurasi API dari environment Angular
import { environment } from './environments/environment'

setApiConfig({
  apiBaseUrl: environment.apiBaseUrl,
  getAuthToken: () => localStorage.getItem('access_token'),
  onUnauthorized: () => { window.location.href = '/login' },
})

platformBrowserDynamic().bootstrapModule(AppModule)

File src/environments/environment.ts:

export const environment = {
  production: false,
  apiBaseUrl: 'https://api.example.com',
}
Komponen Angular (dynamic-form.component.ts)
// dynamic-form.component.ts
import {
  Component,
  OnInit,
  AfterViewInit,
  ViewChild,
  ElementRef,
} from '@angular/core'

@Component({
  selector: 'app-dynamic-form',
  template: `<micro-dynamic-form #formEl></micro-dynamic-form>`,
})
export class DynamicFormComponent implements AfterViewInit {
  @ViewChild('formEl') formRef!: ElementRef

  private formConfig = {
    title: 'Form Angular',
    sections: [
      {
        fields: [
          {
            key: 'username',
            label: 'Username',
            model: 'username',
            component: 'DCodeTextField',
          },
        ],
      },
    ],
    actions: [{ key: 'submit', label: 'Simpan', component: 'DCodeButton' }],
  }

  ngAfterViewInit() {
    const el = this.formRef.nativeElement

    // Set config via DOM property
    el.config = this.formConfig

    el.addEventListener('submit', (event: CustomEvent) => {
      
    })
  }
}
Angular Standalone (Angular 17+)
// app.component.ts
import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core'
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'

@Component({
  selector: 'app-root',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `<micro-dynamic-form #formEl></micro-dynamic-form>`,
})
export class AppComponent implements AfterViewInit {
  @ViewChild('formEl') formRef!: ElementRef

  ngAfterViewInit() {
    const el = this.formRef.nativeElement
    el.config = { /* config Anda di sini */ }
    el.addEventListener('submit', (e: CustomEvent) => 
  }
}

6. Nuxt 3

Di Nuxt 3, gunakan plugin untuk mendaftarkan Web Components dan tandai sebagai custom element.

Plugin (plugins/dynamic-ui.client.ts)

Buat file plugin dengan suffix .client.ts agar hanya dijalankan di sisi klien (browser):

// plugins/dynamic-ui.client.ts
import DynamicUI from '@gemafajarramadhan/dynamic-ui'
import '@gemafajarramadhan/dynamic-ui/style.css'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()

  // ✅ Konfigurasi API dari runtimeConfig Nuxt (env host project)
  nuxtApp.vueApp.use(DynamicUI, {
    apiBaseUrl: config.public.apiBaseUrl,
    getAuthToken: () => useCookie('access_token').value ?? null,
    onUnauthorized: () => navigateTo('/login'),
  })
})
Konfigurasi Nuxt (nuxt.config.ts)
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      apiBaseUrl: process.env.VITE_API_BASE_URL, // set di .env
    },
  },
  vue: {
    compilerOptions: {
      // Arahkan Nuxt/Vue agar tidak menganggap element ini sebagai Vue component
      isCustomElement: (tag: string) => tag.startsWith('micro-'),
    },
  },
})
Penggunaan di Page atau Component
<!-- pages/index.vue -->
<script setup>
import { ref, onMounted } from 'vue'

const formRef = ref(null)

const formConfig = {
  title: 'Form Nuxt',
  sections: [
    {
      fields: [
        {
          key: 'name',
          label: 'Nama',
          model: 'name',
          component: 'DCodeTextField',
        },
      ],
    },
  ],
  actions: [{ key: 'submit', label: 'Submit', component: 'DCodeButton' }],
}

onMounted(() => {
  if (formRef.value) {
    formRef.value.config = formConfig
    formRef.value.addEventListener('submit', (e) => {
      
    })
  }
})
</script>

<template>
  <div>
    <h1>Halaman Dynamic Form</h1>
    <!-- Gunakan ClientOnly agar tidak error saat SSR -->
    <ClientOnly>
      <micro-dynamic-form ref="formRef" />
    </ClientOnly>
  </div>
</template>

Custom Logic Registry

Library menyediakan Logic Registry agar aplikasi host dapat menyuntikkan business logic ke dalam komponen tanpa memodifikasi library.

Option A: via window (sebelum library dimuat)

window.__MICRO_LOGIC_REGISTRY__ = {
  'user-management': {
    onReady(context) {
      
    },
    handleAction(action, payload) {
      if (action === 'delete') {
        // Tampilkan modal konfirmasi
        return true // intercept action
      }
    },
  },
}

Option B: via fungsi registerLogic (setelah import library)

import {
  registerLogic,
  unregisterLogic,
} from '@gemafajarramadhan/dynamic-ui'

registerLogic('user-management', {
  onReady(context) {
    
  },
  handleAction(action, payload) {
    
  },
})

// Untuk menghapus logic saat komponen unmount:
// unregisterLogic('user-management')

Interface LogicModule

interface LogicModule {
  onReady?: (context: Record<string, any>) => void
  handleAction?: (action: string, payload: any) => boolean | void
}

TypeScript Support

Untuk menambahkan type yang benar pada semua framework, buat file deklarasi global:

// src/types/dynamic-ui.d.ts

declare namespace DynamicUI {
  interface LogicModule {
    onReady?: (context: Record<string, any>) => void
    handleAction?: (action: string, payload: any) => boolean | void
  }
}

// Deklarasi untuk custom element di JSX (React / Next.js)
declare global {
  namespace JSX {
    interface IntrinsicElements {
      'micro-dynamic-form': any
      'micro-dynamic-datatable': any
    }
  }

  // Untuk global window registry
  interface Window {
    __MICRO_LOGIC_REGISTRY__?: Record<string, DynamicUI.LogicModule>
    __MICRO_API_BASE_URL__?: string
    __MICRO_AUTH_TOKEN__?: string
  }
}

export {}

Referensi Props Komponen

Berikut adalah dokumentasi lengkap props untuk setiap komponen yang tersedia.


DataTable

Komponen tabel data yang mendukung mode API (fetch otomatis) maupun data statis, dengan pagination, sorting, filtering, dan action button.

Prop Tipe Default Deskripsi
columns TableColumn[] [] Definisi kolom tabel
data any[] [] Data statis (mode non-API)
perPage number 10 Jumlah baris per halaman
loading boolean false Status loading eksternal
hasActions boolean false Tampilkan kolom aksi baris
nested boolean false Mode nested (tampilan kompak tanpa header search)
apiModule string — Module API untuk fetch data
apiAction string 'list' Action API
apiMethod string — HTTP method (GET/POST)
apiUrl string — URL endpoint API langsung
apiParams object {} Parameter tambahan untuk request API
tableConfig any — Konfigurasi JSON lengkap tabel (dari backend)
dynamicColumns boolean false Gunakan kolom dinamis dari response API
enableQuickSearch boolean true Aktifkan pencarian cepat (debounce)
showPagination boolean true Tampilkan pagination
ignorePermissions boolean false Abaikan cek permission
permissionBase string — Base path untuk permission check
<template>
  <!-- Mode API -->
  <DataTable
    api-url="/api/users"
    api-method="POST"
    :columns="[
      { key: 'name', label: 'Nama', sortable: true },
      { key: 'email', label: 'Email' },
    ]"
    :per-page="25"
  />

  <!-- Mode data statis -->
  <DataTable
    :data="myData"
    :columns="columns"
  />
</template>

DCodeButton

Tombol dengan berbagai variant, warna kustom, ikon Lucide, dan tooltip.

Prop Tipe Default Deskripsi
text string '' Teks tombol
icon string — Nama ikon Lucide (e.g. 'Save', 'Trash2')
variant 'default'|'secondary'|'destructive'|'outline'|'ghost'|'link'|'tonal'|'elevated' 'default' Variant tampilan
size 'default'|'sm'|'lg'|'icon' 'default' Ukuran tombol
tooltip string '' Teks tooltip
bgColor string '' Warna background kustom (tailwind/hex)
textColor string '' Warna teks kustom
hoverBgColor string '' Warna background saat hover
borderColor string '' Warna border kustom
disabled boolean false Nonaktifkan tombol
type 'button'|'submit'|'reset' 'button' Tipe HTML button
visible boolean true Tampilkan/sembunyikan tombol
skeleton boolean false Tampilkan skeleton loading
<template>
  <DCodeButton text="Simpan" icon="Save" variant="default" />
  <DCodeButton text="Hapus" icon="Trash2" variant="destructive" tooltip="Hapus data ini" />
  <DCodeButton text="Submit" type="submit" :disabled="isLoading" />
</template>

DCodeTextField

Input teks dengan banyak fitur: filter karakter, transformasi case, clearable, password toggle.

Prop Tipe Default Deskripsi
modelValue string|number|null — Value input (v-model)
prependInner string — Teks/ikon di sisi kiri input
clearable boolean — Tampilkan tombol clear
hideClearWhenEmpty boolean — Sembunyikan tombol clear saat kosong
textCase 'none'|'uppercase'|'lowercase'|'titlecase'|'sentencecase' 'none' Transformasi huruf
applyTextCaseToValue boolean true Terapkan transformasi ke v-model
valueType 'free'|'number'|'letter'|'letternumber'|'email'|'password' 'free' Filter jenis karakter yang diperbolehkan
allowSpaces boolean — Izinkan spasi
allowChars string|string[] — Karakter tambahan yang diizinkan
pattern RegExp — Regex validasi
visible boolean true Tampilkan/sembunyikan
skeleton boolean false Tampilkan skeleton
returnNumber boolean — Emit nilai sebagai number
error string|null — Pesan error validasi
<template>
  <DCodeTextField
    v-model="form.name"
    label="Nama Lengkap"
    placeholder="Masukkan nama..."
    text-case="titlecase"
    clearable
  />
  <DCodeTextField
    v-model="form.email"
    label="Email"
    value-type="email"
    :error="errors.email"
  />
  <DCodeTextField
    v-model="form.password"
    label="Password"
    value-type="password"
  />
</template>

DCodeTextarea

Textarea dengan karakter counter, filter, dan transformasi case.

Prop Tipe Default Deskripsi
modelValue string|number|null — Value (v-model)
clearable boolean — Tombol clear
hideClearWhenEmpty boolean true Sembunyikan clear saat kosong
textCase 'none'|'uppercase'|'lowercase'|'titlecase'|'sentencecase' 'none' Transformasi huruf
applyTextCaseToValue boolean true Terapkan ke v-model
valueType 'free'|'number'|'letter'|'letternumber' 'free' Filter karakter
allowSpaces boolean true Izinkan spasi
allowChars string|string[] — Karakter tambahan
pattern RegExp — Regex validasi
visible boolean true Tampilkan/sembunyikan
skeleton boolean false Skeleton loading
showCharCount boolean true Tampilkan counter karakter
returnNumber boolean false Emit sebagai number
error string|null null Pesan error
<template>
  <DCodeTextarea
    v-model="form.description"
    label="Deskripsi"
    placeholder="Masukkan deskripsi..."
    :show-char-count="true"
    maxlength="500"
  />
</template>

DCodeAutoComplete

Dropdown autocomplete dengan fetch API otomatis dan pencarian.

Prop Tipe Default Deskripsi
modelValue any — Value terpilih (v-model)
endpoint { apiUrl?, apiMethod?, apiModule?, apiAction? } — Konfigurasi endpoint API
apiParams object {} Parameter tambahan request
fetchItems Function — Fungsi custom fetch (alternatif endpoint)
itemTitle string|Function 'name' Field label item
itemValue string|Function 'id' Field nilai item
returnObject boolean false Return objek penuh
autoFetchOnOpen boolean true Fetch saat dropdown terbuka
autoFetchOnMount boolean false Fetch saat mount
clearable boolean false Tombol clear
disabled boolean false Nonaktifkan
visible boolean true Tampilkan/sembunyikan
skeleton boolean false Skeleton loading
error string|null null Pesan error
icon Component — Ikon kiri input
<template>
  <DCodeAutoComplete
    v-model="form.departemenId"
    label="Departemen"
    :endpoint="{ apiUrl: '/api/master/departemen' }"
    item-title="nama"
    item-value="id"
    clearable
  />
</template>

DCodeInfiniteAutoComplete

Autocomplete dengan pagination infinite scroll (load more saat scroll ke bawah).

Prop Tipe Default Deskripsi
modelValue any — Value (v-model)
endpoint { apiUrl?, apiMethod? } — Endpoint API
apiParams object {} Parameter tambahan
fetchItems Function — Custom fetch
itemTitle string|Function 'name' Field label
itemValue string|Function 'id' Field nilai
itemSubtitle string|Function — Field subtitle item
returnObject boolean false Return objek penuh
autoFetchOnOpen boolean true Fetch saat buka
refreshOnOpen boolean false Refresh data setiap buka
clearable boolean false Tombol clear
disabled boolean false Nonaktifkan
visible boolean true Tampilkan
skeleton boolean false Skeleton
error string|null null Error message
icon Component — Ikon prefix
pageSize number 20 Jumlah item per halaman
<template>
  <DCodeInfiniteAutoComplete
    v-model="form.userId"
    label="Pilih User"
    :endpoint="{ apiUrl: '/api/users', apiMethod: 'GET' }"
    item-title="fullName"
    item-value="id"
    :page-size="15"
  />
</template>

DCodeMultiSelect

Dropdown multi-pilihan dengan chip tags dan search.

Prop Tipe Default Deskripsi
modelValue any[] — Array nilai terpilih (v-model)
items object[] [] Data statis
endpoint { apiUrl?, apiMethod?, apiOptions? } — Endpoint API
fetchItems Function — Custom fetch
autoFetchOnOpen boolean true Fetch saat buka
autoFetchOnMount boolean false Fetch saat mount
refreshOnOpen boolean false Refresh setiap buka
label string '' Label dropdown
placeholder string 'Pilih data…' Placeholder
disabled boolean false Nonaktifkan
clearable boolean false Tombol clear
searchable boolean false Aktifkan search
searchPlaceholder string 'Cari…' Placeholder search
visible boolean true Tampilkan
skeleton boolean false Skeleton
maxChips number|null 2 Maks chip ditampilkan
maxSelected number|null 3 Maks pilihan
itemTitle string|Function — Field label
itemValue string|Function — Field nilai
returnObject boolean — Return objek penuh
error string|null — Error message
<template>
  <DCodeMultiSelect
    v-model="form.tags"
    label="Tags"
    :items="availableTags"
    item-title="name"
    item-value="id"
    :max-selected="5"
    searchable
  />
</template>

DCodeDatePicker

Date picker atau date range picker dengan berbagai format.

Prop Tipe Default Deskripsi
modelValue string|[string,string]|null — Value tanggal (v-model)
range boolean true Mode range (pilih 2 tanggal)
autoRangeDays number|null null Otomatis set range N hari
disablePast boolean false Nonaktifkan tanggal masa lalu
maxDaysAhead number|null null Maks hari ke depan
placeholder string 'Pilih tanggal' Placeholder
label string — Label
disabled boolean false Nonaktifkan
clearable boolean true Tombol clear
displayFormat string 'dd/MM/yyyy' Format tampilan
modelType string 'yyyy-MM-dd' Format v-model
locale 'id'|'en' 'en' Bahasa kalender
placement string 'bottom-start' Posisi dropdown
prependInner string — Teks prefix
error string|null null Error message
tone string 'primary' Warna tema kalender
skeleton boolean false Skeleton loading
<template>
  <!-- Single date -->
  <DCodeDatePicker
    v-model="form.tanggal"
    label="Tanggal Lahir"
    :range="false"
    display-format="dd MMMM yyyy"
    locale="id"
  />

  <!-- Date range -->
  <DCodeDatePicker
    v-model="form.periode"
    label="Periode"
    :range="true"
  />
</template>

DCodeCurrencyField

Input angka dengan format mata uang otomatis.

Prop Tipe Default Deskripsi
modelValue number|null — Nilai angka (v-model)
currency string 'IDR' Kode mata uang ('IDR', 'USD')
locale string 'en-US' Locale format angka
label string — Label field
placeholder string — Placeholder
error string|null — Error message
disabled boolean — Nonaktifkan
readonly boolean — Read-only
clearable boolean — Tombol clear
hideClearWhenEmpty boolean true Sembunyikan clear jika kosong
prependInner string — Override simbol mata uang
skeleton boolean false Skeleton loading
<template>
  <DCodeCurrencyField
    v-model="form.harga"
    label="Harga"
    currency="IDR"
    clearable
    :error="errors.harga"
  />
</template>

DCodeCheckbox

Checkbox dengan label dan dukungan indeterminate state.

Prop Tipe Default Deskripsi
modelValue boolean|null — Nilai checkbox (v-model)
visible boolean true Tampilkan
skeleton boolean false Skeleton
indeterminate boolean false State indeterminate
disabled boolean false Nonaktifkan

Gunakan attr label untuk teks di samping checkbox.

<template>
  <DCodeCheckbox
    v-model="form.setuju"
    label="Saya setuju dengan syarat & ketentuan"
    required
  />
</template>

DCodeSwitch

Toggle switch dengan berbagai ukuran, warna, dan tooltip.

Prop Tipe Default Deskripsi
modelValue boolean false Status switch (v-model)
label string '' Label teks
disabled boolean false Nonaktifkan
size 'small'|'medium'|'large' 'medium' Ukuran switch
color 'primary'|'success'|'warning'|'danger' 'primary' Warna aktif
loading boolean false Status loading
labelPosition 'left'|'right' 'right' Posisi label
required boolean false Required
showTooltip boolean false Tampilkan tooltip
tooltipActiveText string 'Aktif' Teks tooltip saat aktif
tooltipInactiveText string 'Tidak Aktif' Teks tooltip saat nonaktif
<template>
  <DCodeSwitch
    v-model="form.isActive"
    label="Status Aktif"
    color="success"
    size="medium"
    :show-tooltip="true"
  />
</template>

DCodeRadio

Grup radio button yang bisa fetch opsi dari API.

Prop Tipe Default Deskripsi
modelValue any — Value terpilih (v-model)
label string 'name' Key field label dari opsi
options any[] [] Opsi data statis
apiUrl string '' URL endpoint API untuk fetch opsi
apiMethod 'GET'|'POST' 'GET' HTTP method
dataPath string '' Path ke array data dalam response
layout boolean true Layout horizontal (true) / vertikal
disabled boolean false Nonaktifkan
required boolean false Required
error string|null null Error message
size 'sm'|'md'|'lg' 'md' Ukuran radio
color string 'blue' Warna accent
model string 'id' Key field nilai dari opsi
<template>
  <!-- Dengan opsi statis -->
  <DCodeRadio
    v-model="form.gender"
    :options="[
      { id: 'L', name: 'Laki-laki' },
      { id: 'P', name: 'Perempuan' },
    ]"
  />

  <!-- Dengan API -->
  <DCodeRadio
    v-model="form.statusId"
    api-url="/api/master/status"
    label="nama"
    model="id"
  />
</template>

DCodeRadioCustom

Radio button dengan tampilan card kustom dan ikon.

Prop Tipe Default Deskripsi
modelValue any — Value (v-model)
icon string — Nama ikon Lucide untuk setiap card
label string 'name' Key field label
options any[] [] Opsi statis
apiUrl string '' URL API
apiMethod 'GET'|'POST' 'GET' HTTP method
dataPath string '' Path data dalam response
layout boolean true Horizontal layout
disabled boolean false Nonaktifkan
required boolean false Required
error string|null null Error message
size 'sm'|'md'|'lg' 'md' Ukuran
color string 'blue' Warna highlight
model string 'id' Key field nilai
<template>
  <DCodeRadioCustom
    v-model="form.plan"
    :options="[
      { id: 'basic', name: 'Basic', icon: 'Star' },
      { id: 'pro', name: 'Pro', icon: 'Zap' },
    ]"
    label="name"
    model="id"
  />
</template>

DCodeOtpInput

Input OTP digit-per-digit dengan submit otomatis ke API.

Prop Tipe Default Deskripsi
modelValue string '' Nilai OTP (v-model)
totalInput number 6 Jumlah digit OTP
default string '' Default nilai awal
label string '' Label
endpoint { apiMethod?, apiUrl? } — Endpoint untuk submit OTP
submitFn Function — Custom submit function

Events: complete (OTP penuh), success, error, loading

<template>
  <DCodeOtpInput
    v-model="otp"
    :total-input="6"
    label="Kode OTP"
    :endpoint="{ apiUrl: '/api/auth/verify-otp', apiMethod: 'POST' }"
    @complete="handleOtpComplete"
  />
</template>

DCodeFileField

Upload file dengan validasi tipe & ukuran, preview, dan upload ke API.

Prop Tipe Default Deskripsi
modelValue File|null — File terpilih (v-model)
label string 'Upload File' Label
hint string — Teks petunjuk
disabled boolean false Nonaktifkan
previewHeight number 190 Tinggi preview (px)
previewWidth number — Lebar preview (px)
allowedTypes string|null null Tipe file diizinkan (e.g. 'pdf,doc')
accept string — Attribute accept HTML
minSizeMb number|null null Ukuran file minimum (MB)
maxSizeMb number 5 Ukuran file maksimum (MB)
endpoint { apiMethod?, apiUrl? } — Endpoint upload
uploadFn Function — Custom upload function
<template>
  <DCodeFileField
    v-model="form.dokumen"
    label="Upload Dokumen"
    allowed-types="pdf,doc,docx"
    :max-size-mb="10"
    :endpoint="{ apiUrl: '/api/upload', apiMethod: 'POST' }"
    @uploaded="handleUploaded"
    @error="handleError"
  />
</template>

DCodeImageField

Upload gambar dengan crop/resize dialog terintegrasi.

Prop Tipe Default Deskripsi
modelValue File|null — File gambar (v-model)
label string 'Upload Gambar' Label
hint string 'Drag & drop atau klik area ini' Petunjuk
disabled boolean false Nonaktifkan
allowedTypes string|null null Tipe gambar diizinkan
accept string 'image/*' Accept HTML
minSizeMb number|null null Min ukuran (MB)
maxSizeMb number 2 Maks ukuran (MB)
title string 'Hasil Gambar' Judul preview
description string '' Deskripsi preview
previewWidth number 360 Lebar preview
previewHeight number 220 Tinggi preview
aspectRatio number|null null Rasio aspek crop
outputWidth number|null null Lebar output (px)
outputHeight number|null null Tinggi output (px)
quality number 0.92 Kualitas output (0-1)
autoOpenEditor boolean true Buka editor saat file dipilih
minWidth number|null null Min lebar gambar
minHeight number|null null Min tinggi gambar
maxWidth number|null null Maks lebar gambar
maxHeight number|null null Maks tinggi gambar
endpoint { apiMethod?, apiUrl? } — Endpoint upload
uploadFn Function — Custom upload
editUseOriginal boolean false Edit dari foto asli (bukan ter-crop)
<template>
  <DCodeImageField
    v-model="form.foto"
    label="Foto Profil"
    :aspect-ratio="1"
    :output-width="300"
    :output-height="300"
    :max-size-m

Related Packages

@gemafajarramadhan/dynamic-ui

Vue 3 Dynamic UI Component Library - Compatible with Vue, React, Angular, and an...

51,640
zhytech-ui

基于Vue3 + ElementPlus + TypeScript封装的组件库

8,425
vue-dynamic-form-component

**vue-dynamic-form-component** is a dynamic form component base on element-ui an...

3,463 279
@unframed/vue

Vue components for Unframed web components, with v-model and typed props.

951 1
dynamic-form-vue-antd

基于vue 和 ant-design-vue的动态表单创建组件

214

Version History

Version Released License
1.0.28 MIT
1.0.27 MIT
1.0.26 MIT
1.0.25 MIT
1.0.23 MIT