Helpers

Auto-imported utility classes. Type in the inputs: the tables call the real functions.

Source in the kit: utils/ · composables/useWatch.ts

NumberHelper

examples/helpers/Number.vue
NumberHelper.withComma(value)1,234,567.891
NumberHelper.withFixed(value)1,234,567.89
NumberHelper.toCurrency(value)1,234,567.89
NumberHelper.toCurrency(value, 'THB')THB 1,234,567.89
NumberHelper.toNumber(value)1234567.891
<template>
  <div class="space-y-3">
    <Input
      v-model="input"
      class="w-56"
    />
    <table class="w-full text-sm">
      <tbody class="divide-y divide-gray-100">
        <tr
          v-for="row in rows"
          :key="row.call"
        >
          <td class="py-1.5 pr-4 font-mono text-xs text-gray-500">
            {{ row.call }}
          </td>
          <td class="py-1.5 font-medium">
            {{ row.result }}
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script lang="ts" setup>
const input = ref('1234567.891')

// All of them accept a number or a numeric string, and treat empty input as 0
const rows = computed(() => [
  {
    call: 'NumberHelper.withComma(value)',
    result: NumberHelper.withComma(input.value),
  },
  {
    call: 'NumberHelper.withFixed(value)',
    result: NumberHelper.withFixed(input.value),
  },
  {
    call: 'NumberHelper.toCurrency(value)',
    result: NumberHelper.toCurrency(input.value),
  },
  {
    call: "NumberHelper.toCurrency(value, 'THB')",
    result: NumberHelper.toCurrency(input.value, 'THB'),
  },
  {
    call: 'NumberHelper.toNumber(value)',
    result: NumberHelper.toNumber(input.value),
  },
])
</script>

TimeHelper

examples/helpers/Time.vue

Formats, time zone and Thai year come from app.config.ts . This playground sets is_thai_year: true .

TimeHelper.displayDate(value)21 ก.พ. 2567
TimeHelper.displayDateTime(value)21 ก.พ. 2567 01:30
TimeHelper.displayDateThai(value)21 ก.พ. 2567
TimeHelper.displayDateRange(value, value).startDate21 ก.พ. 2567
TimeHelper.getDateFormTime(value)2024-02-20
TimeHelper.getDateTimeFormTime(value)2024-02-20 18:30:00
TimeHelper.getTimeFormTime(value)18:30
TimeHelper.getISODateTimeFormTime(value)2024-02-20T18:30:00Z
TimeHelper.toUTC(value)2024-02-20 18:30:00
TimeHelper.getCurrentDate()2026-11-06
<template>
  <div class="space-y-3">
    <Input
      v-model="input"
      class="w-72"
    />
    <table class="w-full text-sm">
      <tbody class="divide-y divide-gray-100">
        <tr
          v-for="row in rows"
          :key="row.call"
        >
          <td class="py-1.5 pr-4 font-mono text-xs text-gray-500">
            {{ row.call }}
          </td>
          <td class="py-1.5 font-medium">
            {{ row.result ?? 'null' }}
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script lang="ts" setup>
// Any ISO string or Date. Try an empty value: every function answers null instead of "Invalid Date".
const input = ref('2024-02-20T18:30:00Z')

// display* are for people: core.date_format / date_time_format, core.time_zone, Thai year and month if enabled.
// get* are for the API: core.date_format_system / date_time_format_system.
const rows = computed(() => [
  {
    call: 'TimeHelper.displayDate(value)',
    result: TimeHelper.displayDate(input.value),
  },
  {
    call: 'TimeHelper.displayDateTime(value)',
    result: TimeHelper.displayDateTime(input.value),
  },
  {
    call: 'TimeHelper.displayDateThai(value)',
    result: TimeHelper.displayDateThai(input.value),
  },
  {
    call: 'TimeHelper.displayDateRange(value, value).startDate',
    result: TimeHelper.displayDateRange(input.value, input.value).startDate,
  },
  {
    call: 'TimeHelper.getDateFormTime(value)',
    result: TimeHelper.getDateFormTime(input.value),
  },
  {
    call: 'TimeHelper.getDateTimeFormTime(value)',
    result: TimeHelper.getDateTimeFormTime(input.value),
  },
  {
    call: 'TimeHelper.getTimeFormTime(value)',
    result: TimeHelper.getTimeFormTime(input.value),
  },
  {
    call: 'TimeHelper.getISODateTimeFormTime(value)',
    result: TimeHelper.getISODateTimeFormTime(input.value),
  },
  {
    call: 'TimeHelper.toUTC(value)',
    result: TimeHelper.toUTC(input.value),
  },
  {
    call: 'TimeHelper.getCurrentDate()',
    result: TimeHelper.getCurrentDate(),
  },
])
</script>
ArrayHelper.toOptions(provinces)[{"value":10,"label":"Bangkok"},{"value":50,"label":"Chiang Mai"}]
ArrayHelper.toOptions(provinces, 'meta.code', 'name')[{"value":"BKK","label":"Bangkok"},{"value":"CNX","label":"Chiang Mai"}]
ArrayHelper.toArray(null)[]
ArrayHelper.isEmpty([])true
ObjectHelper.toOption(provinces[0]){"value":10,"label":"Bangkok"}
ObjectHelper.isEmpty({})true
StringHelper.getError(apiError)"Email is already taken"
StringHelper.getError(null, 'Try again later')"Try again later"
ObjectHelper.isInvalidParams(apiError)true
ParamHelper.isNotFoundError({ code: 'NOT_FOUND' })true
StringHelper.truncate('A very long sentence', 10)"A very lon..."
StringHelper.joinURL('https://api.test/', '/users', '1')"https://api.test/users/1"
<template>
  <table class="w-full text-sm">
    <tbody class="divide-y divide-gray-100">
      <tr
        v-for="row in rows"
        :key="row.call"
      >
        <td class="py-1.5 pr-4 align-top font-mono text-xs text-gray-500">
          {{ row.call }}
        </td>
        <td class="py-1.5 font-mono text-xs">
          {{ JSON.stringify(row.result) }}
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script lang="ts" setup>
const provinces = [
  {
    id: 10,
    name: 'Bangkok',
    meta: {
      code: 'BKK',
    },
  },
  {
    id: 50,
    name: 'Chiang Mai',
    meta: {
      code: 'CNX',
    },
  },
]

const apiError = {
  code: 'INVALID_PARAMS',
  message: 'Invalid parameters',
  fields: {
    email: {
      message: 'Email is already taken',
    },
  },
}

const rows = [
  {
    // API list -> select options. Attribute names can be paths.
    call: 'ArrayHelper.toOptions(provinces)',
    result: ArrayHelper.toOptions(provinces),
  },
  {
    call: "ArrayHelper.toOptions(provinces, 'meta.code', 'name')",
    result: ArrayHelper.toOptions(provinces, 'meta.code', 'name'),
  },
  {
    call: 'ArrayHelper.toArray(null)',
    result: ArrayHelper.toArray(null),
  },
  {
    call: 'ArrayHelper.isEmpty([])',
    result: ArrayHelper.isEmpty([]),
  },
  {
    call: 'ObjectHelper.toOption(provinces[0])',
    result: ObjectHelper.toOption(provinces[0]),
  },
  {
    call: 'ObjectHelper.isEmpty({})',
    result: ObjectHelper.isEmpty({}),
  },
  {
    // The one to remember: turns any API error into a sentence for the user
    call: 'StringHelper.getError(apiError)',
    result: StringHelper.getError(apiError),
  },
  {
    call: "StringHelper.getError(null, 'Try again later')",
    result: StringHelper.getError(null, 'Try again later'),
  },
  {
    call: 'ObjectHelper.isInvalidParams(apiError)',
    result: ObjectHelper.isInvalidParams(apiError),
  },
  {
    call: "ParamHelper.isNotFoundError({ code: 'NOT_FOUND' })",
    result: ParamHelper.isNotFoundError({
      code: 'NOT_FOUND',
    }),
  },
  {
    call: "StringHelper.truncate('A very long sentence', 10)",
    result: StringHelper.truncate('A very long sentence', 10),
  },
  {
    call: "StringHelper.joinURL('https://api.test/', '/users', '1')",
    result: StringHelper.joinURL('https://api.test/', '/users', '1'),
  },
]
</script>
isOpen = false
<template>
  <div class="space-y-3">
    <div class="flex items-center gap-3">
      <Switch v-model="isOpen" />
      <span class="text-sm">isOpen = {{ isOpen }}</span>
    </div>
    <ul class="text-sm text-gray-600">
      <li
        v-for="(entry, index) in log"
        :key="index"
      >
        {{ entry }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts" setup>
const isOpen = ref(false)
const log = ref<string[]>([])

// A plain watch fires on every change, so callbacks start with `if (value)`.
// These fire only on the transition you care about, which is how pages react to loader status.
useWatchTrue(
  () => isOpen.value,
  () => log.value.push('useWatchTrue: became true')
)
useWatchFalse(
  () => isOpen.value,
  () => log.value.push('useWatchFalse: became false')
)
useWatchChange(
  () => isOpen.value,
  (value, oldValue) => log.value.push(`useWatchChange: ${oldValue} -> ${value}`)
)
</script>