@skyservice-developers/vue-dev-kit
| Install | |
|---|---|
npm install @skyservice-developers/vue-dev-kit |
|
| Latest Version: | 3.5.0 |
| Vue: | 3 |
| Older releases: | 2 |
| License: | MIT |
| Last Updated: | Oct 22, 2026 |
| Links: | GitHub · npm |
@skyservice-developers/vue-dev-kit
Vue 3 developer toolkit для Skyservice mini-apps: UI компоненти + TypeScript SDK (iframe bridge + HTTP API клієнт).
📖 Документація з живими демо: https://74cfe434-a2b3-4d49-8fa7-db7343e399dc.apps.platform365.online/
v2.0.0 breaking changes:
- Vue 2 більше не підтримується (тільки Vue 3.4+).
- Додано повноцінний SDK (
bridge+SkyserviceAPI), раніше опублікований як окремий пакетskyservice-sdk(задепрекейтнутий).- Старі utils
shared/utils/parentBridgeтаshared/utils/webviewCheckзамінені типізованим SDK. Якщо ви імпортували їх напряму — переходьте на@skyservice-developers/vue-dev-kit/sdk.
Встановлення
npm install @skyservice-developers/vue-dev-kit
Підключення
Компоненти + SDK (разом)
import '@skyservice-developers/vue-dev-kit/style.css'
import {
// shared/ui
Header, Modal, Dialog,
SkyButton, SkySelect, SkySelectSearch, SkyInput, SkySearchInput, SkyCheckbox, SkyTabs,
SkyAlert, SkyBadge, SkyLoader,
SkyCard, SkyCardHeader, SkyCardRow,
SkyPageHeader, SkyStatCard,
SkyTable,
// грід на TanStack + примітиви таблиці + композабли
SkyDataTable, createSkyColumnHelper, skyTableFeatures,
SkyTableRoot, SkyTableHeader, SkyTableHead, SkyTableBody, SkyTableVirtualBody,
SkyTableRow, SkyTableCell,
useTableSort, useTableSelection, useColumnVisibility,
FunctionalCalendar, SkyDateRangePicker,
SkyFilterDropdown, SkyPagination,
// widgets
SkyTileCard,
// features
SkyCheckboxFilter, SkySelectFilter,
TableMassActions, TableColumnSettings, TableVirtualBody,
// sdk
navigate, SkyserviceAPI, isInsideIframe, formatMoney, currencyIcon,
// sky-service-ui-components
NotificationElement,
notificationModule, globalStore,
} from '@skyservice-developers/vue-dev-kit'
Тільки SDK (без Vue залежностей)
import {
navigate, exit, getToken, getCompany,
setRocketMode, notify, SkyserviceAPI,
isInsideIframe, webviewCheck,
} from '@skyservice-developers/vue-dev-kit/sdk'
Шлях
/vue3залишено як alias на корінь для зворотної сумісності з v1.
SDK
Bridge — комунікація з Dashboard через iframe
Mini-app працює в iframe всередині Dashboard. Bridge обгортає window.postMessage типізованими функціями.
import {
navigate, exit, getBack,
getStoreData, getLocalStorageData, getWindowData,
getCompany, getUser, getToken, getLang, getProductCategories,
setLocalStorage, setRocketMode,
trackVisit, openExternalLink, openCrispChat,
notify, notifyError, notifyWarn,
isInsideIframe, onMessage,
setSenderId, getSenderId,
} from '@skyservice-developers/vue-dev-kit/sdk'
const token = await getToken() // → string | null
const company = await getCompany() // → об'єкт з Vuex store
setLocalStorage('userPref', { theme: 'dark' })
notify('Збережено')
navigate('/products/42')
Всі гетери повертають Promise<T | null> — null при таймауті або якщо додаток відкритий поза iframe. null не ламає await — просто треба перевірити результат.
SkyserviceAPI — HTTP клієнт
Прямі запити до Skyservice API. Працює всюди (браузер, Node, edge), не потребує iframe.
import { SkyserviceAPI, getToken } from '@skyservice-developers/vue-dev-kit/sdk'
const token = await getToken()
const api = SkyserviceAPI.create({
token,
domain: 'api.skyservice.online',
companyId,
appId,
// developerId: 'optional',
// deploymentId + appName — тільки якщо користуєтесь setAppActive
deploymentId: '4c3c7d32-2408-503b-c439-bff7332a238e',
appName: 'glovo',
})
const tradepoints = await api.getTradepoints()
const categories = await api.getCategoryTree(tradepointId)
const products = await api.getProducts({ tradepointId })
// Права доступу
const canInventory = await api.getPerms('204') // → boolean
const allPerms = await api.getPerms() // → PermsMap { '204': true, ... }
// Активація/деактивація міні-додатку
await api.setAppActive({ isActive: true, title: 'Glovo', settings: { foo: 'bar' } })
await api.setAppActive({ isActive: false, title: 'Glovo' })
SkyserviceAPI.create()— factory; прямий викликnew SkyserviceAPI(...)недоступний.setAppActivePOST наapi.cabinet.developer.skyservice.online/index.php. ЯкщоdeploymentId+appNameзадані в конфізі — додатково шлеsendActiveApppostMessage в parent (Dashboard) з відповіддю сервера та новим станом.
Права доступу — getPerms
Повертає права поточного користувача. Джерело за пріоритетом:
- Стор Dashboard через iframe bridge (
store.perms) — миттєво, без токена, працює всередині iframe; - HTTP fallback
section=adminPanel&action=getStart→data.settings.perms— поза iframe (standalone-сторінка, Node) або поки стор ще порожній.
await api.getPerms('9006') // → boolean — чи надано право 9006
await api.getPerms() // → PermsMap: { '9006': true, '100': false, ... }
api.clearPermsCache() // скинути кеш (наступний getPerms перезапросить джерело)
Результат кешується на інстансі — повторні виклики не роблять зайвих запитів/postMessage. Значення нормалізуються в
boolean(бек може віддавати0/1); відсутній код →false.
Webview detection
import {
isInsideIframe, isWebview,
isIosWebview, isAndroidWebview, isCefWebview,
webviewCheck,
} from '@skyservice-developers/vue-dev-kit/sdk'
Компоненти
Header
Шапка сторінки з кнопкою "Назад", дропдауном нещодавніх розділів та слотом для кнопок. Автоматично відправляє батьківському iframe сигнал setRocketMode при монтуванні та відновлює попередній стан при розмонтуванні.
<Header
title="Товари"
subtitle="Управління каталогом"
:dropdown-items="recentPages"
@navigate="goTo"
>
<SkyButton @click="openDialog">+ Додати</SkyButton>
</Header>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
title |
String |
'' |
Заголовок сторінки |
subtitle |
String |
'' |
Підзаголовок |
showBackButton |
Boolean |
true |
Показувати кнопку "Назад" (тільки в iframe) |
backButtonTitle |
String |
'Назад' |
Tooltip кнопки "Назад" |
backEvent |
Function |
null |
Кастомна функція для кнопки "Назад" (замість iframe exit) |
dropdownItems |
Array |
[] |
Список нещодавніх розділів [{ name, path, lastVisit }] |
dropdownTitle |
String |
'Останні відвідані розділи' |
Заголовок дропдауну |
visitLabel |
String |
'Останнє відвідування' |
Підпис часу в дропдауні |
trackPageName |
String |
'' |
Назва сторінки для трекінгу відвідувань |
trackPagePath |
String |
'' |
Шлях сторінки для трекінгу (за замовчуванням /apps/{trackPageName}; актуальний дашборд підставляє свій реальний маршрут) |
appId |
String |
'' |
Ідентифікатор додатку для iframe bridge |
Slots
| Slot | Опис |
|---|---|
default |
Кнопки та елементи справа |
title |
Кастомний заголовок |
subtitle |
Кастомний підзаголовок |
Events
| Event | Опис |
|---|---|
back |
Клік на кнопку "Назад" |
navigate |
Вибір розділу з дропдауну |
Modal
Модальне вікно з оверлеєм, шапкою, скролом у тілі та опціональним футером. Монтується в <body>.
<button @click="show = true">Відкрити</button>
<Modal v-model="show" title="Заголовок" subtitle="Підзаголовок">
<p>Контент модального вікна</p>
<template #footer>
<button @click="show = false">Закрити</button>
</template>
</Modal>
Шапку можна зібрати слотами — так само як у Header: #title / #subtitle
перекривають відповідні props, #header-actions додає елементи справа
(кнопка "Назад" лишається на місці).
<Modal v-model="show">
<template #title>
<h4 style="margin: 0">Замовлення №1042 <SkyBadge tone="success" label="Оплачено" /></h4>
</template>
<template #subtitle>Оновлено щойно</template>
<template #header-actions>
<SkyButton variant="outline">Друк</SkyButton>
</template>
<p>Контент</p>
</Modal>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
Boolean |
false |
Стан відкриття (v-model) |
title |
String |
'' |
Заголовок |
subtitle |
String |
'' |
Підзаголовок |
closeTitle |
String |
'Закрити' |
Tooltip кнопки закриття |
closeOnOverlay |
Boolean |
true |
Закривати при кліку на оверлей |
closeOnEsc |
Boolean |
true |
Закривати при натисканні Esc |
width |
String |
'100%' |
Ширина модалки |
height |
String |
'100%' |
Висота модалки |
borderRadius |
String |
'12px' |
Радіус заокруглення модалки (ігнорується в повноекранному режимі) |
Slots
| Slot | Опис |
|---|---|
default |
Основний контент |
title |
Кастомний заголовок (замість пропа title) |
subtitle |
Кастомний підзаголовок (замість пропа subtitle) |
header-actions |
Елементи справа в шапці (кнопки, бейджі) |
footer |
Футер з кнопками |
CSS змінні
| Змінна | За замовчуванням | Опис |
|---|---|---|
--sky-modal-header-actions-gap |
8px |
Відступ між елементами в #header-actions |
Events
| Event | Опис |
|---|---|
update:modelValue |
Зміна стану |
close |
Закриття модалки |
Dialog
Повноекранний діалог. Є два стилі: next (кнопка "Назад") та classic (кнопка ×). Якщо mode не вказано — визначається автоматично за URL-параметром ?rocketMode=.
<Dialog v-model="show" mode="next" title="Новий товар" subtitle="Заповніть дані">
<div style="padding: 20px">
<input placeholder="Назва" />
</div>
<template #buttons>
<button @click="show = false">Скасувати</button>
<button @click="save">Зберегти</button>
</template>
</Dialog>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
Boolean |
false |
Стан відкриття (v-model) |
title |
String |
'' |
Заголовок |
subtitle |
String |
'' |
Підзаголовок |
mode |
String |
null |
'next' | 'classic' | null (авто) |
closeText |
String |
'' |
Текст кнопки закриття |
closeOnEsc |
Boolean |
true |
Закривати при Esc |
zIndex |
Number|String |
null |
Кастомний z-index |
Slots
| Slot | Опис |
|---|---|
default |
Основний контент |
title |
Кастомний заголовок (замість пропа title) |
subtitle |
Кастомний підзаголовок (замість пропа subtitle) |
header-actions |
Елементи справа в шапці (кнопки, бейджі) |
buttons |
Кнопки у футері |
Слоти шапки працюють в обох режимах — next (ліворуч кнопка "Назад") і
classic (праворуч ×); #header-actions рендериться ліворуч від ×.
<Dialog v-model="show" mode="next">
<template #title>Замовлення №1042</template>
<template #subtitle>3 позиції · 1 240 ₴</template>
<template #header-actions>
<SkyButton variant="outline">Друк</SkyButton>
</template>
<div style="padding: 20px">Контент</div>
</Dialog>
CSS змінні
| Змінна | За замовчуванням | Опис |
|---|---|---|
--sky-dialog-header-actions-gap |
8px |
Відступ між елементами в #header-actions |
--sky-dialog-title-max-width-with-actions |
50% |
Ширина заголовка в classic, коли є #header-actions |
Events
| Event | Опис |
|---|---|
update:modelValue |
Зміна стану |
close |
Закриття |
save |
Підтвердження |
SkyButton
Кнопка з варіантами, станами loading/disabled, режимами block та icon.
<SkyButton variant="primary" @click="save">Зберегти</SkyButton>
<SkyButton variant="danger" :loading="deleting" @click="del">Видалити</SkyButton>
<SkyButton variant="outline" disabled>Недоступно</SkyButton>
<SkyButton variant="secondary" block>На всю ширину</SkyButton>
<!-- Іконка -->
<SkyButton variant="primary" icon title="Додати">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M8 2v12M2 8h12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</SkyButton>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
variant |
String |
'primary' |
'primary' | 'danger' | 'secondary' | 'outline' | 'accent' | 'warning' | 'info' | 'dark' | 'outline-primary' | 'outline-danger' | 'outline-secondary' | 'outline-accent' | 'outline-warning' | 'outline-info' | 'outline-dark' |
loading |
Boolean |
false |
Показує спінер, блокує клік |
disabled |
Boolean |
false |
Вимкнена кнопка |
block |
Boolean |
false |
Повна ширина |
icon |
Boolean |
false |
Квадратна кнопка для іконки |
CSS змінні
--sky-btn-padding: 16px 20px
--sky-btn-radius: 6px
--sky-btn-font-size: 14px
--sky-btn-font-weight: 500
--sky-btn-primary-bg: #24973f
--sky-btn-danger-bg: #dc2626
--sky-btn-secondary-bg: #f3f4f6
--sky-btn-outline-bg: transparent
--sky-btn-accent-bg: #0d6efd
--sky-btn-warning-bg: #ffc107
--sky-btn-info-bg: #0dcaf0
--sky-btn-dark-bg: #212529
--sky-btn-outline-primary-color: #00c279 /* аналогічно для outline-danger, outline-accent … */
SkySelect
Нативний <select> у фірмовому стилі, зі станами default | success | error та підказкою під полем.
<SkySelect
v-model="payment"
:options="[
{ value: 'cash', text: 'Готівка' },
{ value: 'card', text: 'Картка' },
]"
placeholder="Оберіть спосіб оплати"
/>
<SkySelect v-model="payment" :options="options" state="error" hint="Оберіть спосіб" />
<SkySelect v-model="payment" :options="options" disabled />
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
string | number |
— | Поточне значення (v-model) |
options |
Array<{ value: string | number; text: string }> |
— | Список опцій (обов'язковий) |
id |
String |
— | HTML id |
disabled |
Boolean |
false |
Вимкнений стан |
state |
'default' | 'success' | 'error' |
'default' |
Візуальний стан рамки |
placeholder |
String |
'' |
Текст-заглушка (рендериться як disabled hidden опція) |
hint |
String |
'' |
Підказка під полем у кольорі state |
Events
| Event | Payload | Опис |
|---|---|---|
update:modelValue |
string | number |
Emit при виборі опції |
Формат опцій — тільки
{ value, text }. Масив рядків або{ label, value }дадуть порожні пункти: компонент читаєoption.valueіoption.text. Мапте дані заздалегідь —units.map(u => ({ value: u, text: u })).
Список малює браузер, тож клавіатура й поведінка на мобільних — рідні для платформи, а дропдаун не обрізається контейнерами з
overflow: hidden(усерединіModalteleport не потрібен). Компонент займає всю ширину контейнера — обмежуйте батьківський блок, якщо треба вужче.
SkySelectSearch
Кастомний select з полем пошуку у дропдауні — для довгих списків опцій. Дизайн 1:1 зі SkySelect, той самий формат опцій ({ value, text }) та стани. Не використовує нативний <select>, тому працює на старих браузерах (перевірено з Chromium 84): без flex gap, inset, :focus-visible, Array.at() тощо.
Панель не вилазить за нижній край вікна: якщо знизу тісно, вона розкривається вгору, а висоту обмежує під доступне місце — скролиться список усередині. Сторона переобирається на скрол і ресайз, поки панель відкрита. Teleport-а немає, тож контейнер з overflow: hidden (напр. Modal) панель усе одно обріже — там беріть SkyFilterDropdown.
Поле пошуку з'являється від 6 опцій (searchThreshold): на коротких списках воно лише шумить. Коли пошуку немає, стрілками й Enter керує сам тригер — фокус нікуди не переносимо.
<SkySelectSearch
v-model="selected"
:options="[
{ value: 'kyiv', text: 'Київ' },
{ value: 'lviv', text: 'Львів' },
{ value: 'odesa', text: 'Одеса' },
]"
placeholder="Оберіть місто"
search-placeholder="Почніть вводити…"
/>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
string | number |
undefined |
Поточне значення (v-model) |
options |
Array<{ value: string | number; text: string }> |
— | Список опцій (обов'язковий) |
id |
String |
undefined |
id тригера |
disabled |
Boolean |
false |
Вимкнений стан |
state |
'default' | 'success' | 'error' |
'default' |
Візуальний стан |
placeholder |
String |
'' |
Текст, коли нічого не обрано |
hint |
String |
'' |
Підказка під полем |
searchPlaceholder |
String |
'Пошук…' |
Placeholder поля пошуку |
noResultsText |
String |
'Нічого не знайдено' |
Текст за відсутності збігів |
searchThreshold |
Number |
6 |
Від скількох опцій показувати поле пошуку. 0 — завжди, Infinity — ніколи |
Events
| Event | Payload | Опис |
|---|---|---|
update:modelValue |
string | number |
Emit при виборі опції |
Клавіатура
| Клавіша | Дія |
|---|---|
Enter / Space / ↓ |
Відкрити дропдаун (на тригері) |
↑ / ↓ |
Навігація по відфільтрованих опціях |
Enter |
Вибрати підсвічену опцію |
Esc |
Закрити дропдаун |
Tab |
Закрити і перейти далі |
Примітка: дропдаун позиціонується
position: absolute(без teleport), тому в контейнерах зoverflow: hidden(напр. усередині модалки) його може обрізати — так само, як і нативнийSkySelect.
SkyInput
Текстовий input з підтримкою станів default | success | error та опціональною підказкою.
<SkyInput v-model="name" placeholder="Імʼя" />
<SkyInput v-model="email" type="email" state="error" hint="Невалідний email" />
<SkyInput v-model="phone" disabled />
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
String|Number |
'' |
Значення (v-model) |
type |
String |
'text' |
HTML-тип input |
placeholder |
String |
'' |
Текст-заглушка |
id |
String |
— | HTML id |
disabled |
Boolean |
false |
Вимкнений стан |
state |
String |
'default' |
'default' | 'success' | 'error' |
hint |
String |
— | Підказка під input (фарбується в колір state) |
SkySearchInput
Стилізоване поле пошуку (іконка-лупа + кнопка очищення) — 1:1 з дизайном пошуку в адмінках Skyservice. Компонент чисто UI: сам не фільтрує дані, лише віддає значення через v-model — умови пошуку прописує споживач компонента. Підтримує режим collapsible: поле згорнуте до іконки і розгортається по кліку, згортається назад по кліку поза межами компонента (якщо порожнє) або по кліку на іконку.
<SkySearchInput v-model="search" placeholder="Пошук..." />
<!-- Згорнутий режим: розгортається по кліку -->
<SkySearchInput v-model="search" placeholder="Пошук..." collapsible />
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
String |
'' |
Значення (v-model) |
placeholder |
String |
'' |
Текст-заглушка |
id |
String |
— | HTML id |
disabled |
Boolean |
false |
Вимкнений стан |
collapsible |
Boolean |
false |
Якщо true — поле за замовчуванням згорнуте до іконки, розгортається по кліку |
clearAriaLabel |
String |
'Очистити' |
aria-label кнопки очищення |
Events
| Event | Payload | Опис |
|---|---|---|
update:modelValue |
string |
Emit при введенні тексту |
Примітка: фільтрація/пошук по даних — відповідальність батьківського компонента (напр.
computedз.filter()),SkySearchInputлише відображає поле і керує його розгорнутим/згорнутим станом.
SkyCheckbox
Чекбокс з двома режимами: класичний бокс або switch-перемикач. Підтримує v-model з Boolean (одиничний) або масивом значень (через value prop).
<!-- Boolean -->
<SkyCheckbox v-model="agreed">Погоджуюсь з умовами</SkyCheckbox>
<!-- Масив значень -->
<SkyCheckbox v-for="opt in options" :key="opt.value" v-model="selected" :value="opt.value">
{{ opt.name }}
</SkyCheckbox>
<!-- Switch -->
<SkyCheckbox v-model="enabled" switch>Сповіщення</SkyCheckbox>
<SkyCheckbox v-model="disabled" disabled>Заблоковано</SkyCheckbox>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
Boolean|Array |
— | Стан/масив вибраних значень (v-model) |
value |
String|Number |
— | Значення для array-режиму (обов'язкове в ньому) |
switch |
Boolean |
false |
Режим switch замість чекбоксу |
disabled |
Boolean |
false |
Вимкнений стан |
indeterminate |
Boolean |
false |
Третій стан — «обрано частину»; малює риску, на checked не впливає |
Атрибути, які не class/style, їдуть на сам <input> — name, required,
tabindex, aria-* стосуються контрола. class і style лишаються на корені,
бо ними позиціонують обгортку.
Slots
| Slot | Опис |
|---|---|
default |
Лейбл |
SkyTabs
Сегментовані таби з анімованим індикатором: біла «пігулка» плавно переїжджає під активну опцію й підлаштовує ширину під довжину тексту.
<SkyTabs
v-model="period"
:options="[
{ value: 'day', text: 'День' },
{ value: 'week', text: 'Тиждень' },
{ value: 'month', text: 'Місяць' },
]"
/>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
modelValue |
string | number |
— | Активне значення (v-model) |
options |
Array<{ value: string | number; text: string }> |
— | Список табів (обов'язковий) |
disabled |
Boolean |
false |
Блокує весь набір |
Events
| Event | Payload | Опис |
|---|---|---|
update:modelValue |
string | number |
Emit при виборі таба |
Індикатор вимірює активну кнопку через
getBoundingClientRect(): на першому рендері стає на місце без анімації, далі — з переходом. Реагує наresizeвікна і зміну кількості опцій. У прихованому контейнері (display: none) розміри нульові — позиція виправиться після показу.
SkyAlert
Інформаційне повідомлення з 4 тональностями та опціональною іконкою.
<SkyAlert tone="success">Все збережено</SkyAlert>
<SkyAlert tone="error" :show-icon="false">Щось пішло не так</SkyAlert>
<SkyAlert tone="warning">Увага: дія незворотня</SkyAlert>
<SkyAlert tone="info">Підказка для користувача</SkyAlert>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
tone |
String |
'info' |
'success' | 'error' | 'warning' | 'info' |
showIcon |
Boolean |
true |
Показувати іконку ліворуч |
Slots
| Slot | Опис |
|---|---|
default |
Вміст повідомлення |
SkyBadge
Компактний статус-лейбл з крапкою.
<SkyBadge tone="success" label="Під'єднано" />
<SkyBadge tone="error">Помилка</SkyBadge>
<SkyBadge tone="pending" :dot="false" label="Очікування" />
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
tone |
String |
'default' |
'success' | 'error' | 'warning' | 'pending' | 'info' | 'default' |
label |
String |
'' |
Текст (якщо не передаєте slot) |
dot |
Boolean |
true |
Показувати крапку-індикатор |
Slots
| Slot | Опис |
|---|---|
default |
Альтернатива до label |
SkyCard
Shell-контейнер для карток: ribbon / header / body / footer — всі через slots. Використовуйте у зв'язці з SkyCardHeader, SkyCardRow, SkyBadge, SkyAlert.
<SkyCard>
<template #ribbon>Тимчасово закрито</template>
<template #header>
<SkyCardHeader
title="Торгова точка"
subtitle="вул. Хрещатик, 1"
icon-src="/svg/home.svg"
/>
</template>
<SkyCardRow label="Статус">
<SkyBadge tone="success" label="Активна" />
</SkyCardRow>
<SkyCardRow label="ID" value="12345" />
<SkyAlert tone="error">Помилка синхронізації</SkyAlert>
<template #footer>
<SkyButton variant="primary" block>Налаштувати</SkyButton>
</template>
</SkyCard>
Slots
| Slot | Опис |
|---|---|
ribbon |
Стрічка зверху (наприклад "Тимчасово закрито") |
header |
Шапка картки (часто SkyCardHeader) |
default |
Тіло картки (часто SkyCardRow, SkyAlert) |
footer |
Футер з кнопками |
CSS змінні
--sky-card-bg: #fff
--sky-card-radius: 12px
--sky-card-shadow: 0 1px 12px rgba(0,0,0,0.1)
--sky-card-ribbon-bg: #e65100
--sky-card-ribbon-color: #fff
--sky-card-padding-header: 24px 24px 0
--sky-card-padding-body: 10px 24px 15px
--sky-card-padding-footer: 0 24px 24px
SkyPageHeader
Шапка сторінки: заголовок + опис, під ними slot header (кнопки, фільтри, таби).
<SkyPageHeader title="Замовлення" description="Усі замовлення за період">
<template #header>
<SkyButton>Новий</SkyButton>
</template>
</SkyPageHeader>
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
title |
String |
'' |
Заголовок |
description |
String |
'' |
Опис під заголовком |
position |
String |
'bottom' |
Slot header: 'bottom' — під описом, 'side' — збоку в одному рядку |
Slot: header — вміст під заголовком. CSS-змінні: --sky-page-header-title-color, --sky-page-header-description-color.
SkyStatCard
Картка зі статистикою: іконка (необов'язкова) + заголовок + значення. Може бути статичною або клікабельною.
<SkyStatCard title="Всього чеків" value="128 шт" icon-src="/img/statistic-moneys.svg" clickable @click="openChecks" />
<SkyStatCard title="Виручка" value="12 400 ₴" />
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
title |
String |
'' |
Підпис (сірий, зверху) |
value |
String|Number |
'' |
Значення (жирним) |
iconSrc |
String |
'' |
Шлях до іконки; без нього і без slot icon блок іконки не рендериться |
clickable |
Boolean |
false |
Hover-тінь, role="button", Enter/Space, емітить click |
Slots: icon (замість iconSrc), default (замість value). Event: click. CSS-змінні: --sky-stat-card-bg, --sky-stat-card-border, --sky-stat-card-title-color, --sky-stat-card-value-color, --sky-stat-card-icon-bg, --sky-stat-card-icon-border, --sky-stat-card-focus.
SkyCardHeader
Готова шапка картки: іконка + заголовок + підзаголовок.
<SkyCardHeader title="Glovo" subtitle="Інтеграція доставки" icon-src="/svg/glovo.svg" />
<!-- або свій slot замість img -->
<SkyCardHeader title="Меню">
<template #icon>
<MyCustomIcon />
</template>
</SkyCardHeader>
Props
| Prop | Тип | Обов'язково | Опис |
|---|---|---|---|
title |
String |
так | Заголовок |
subtitle |
String |
ні | Підзаголовок |
iconSrc |
String |
ні | Шлях до зображення іконки |
Slots
| Slot | Опис |
|---|---|
icon |
Override іконки (якщо не хочете використовувати iconSrc) |
CSS змінні
--sky-card-icon-size: 64px
--sky-card-icon-radius: 12px
--sky-card-icon-bg: #00c279
--sky-card-icon-color: #fff
SkyLoader
Фірмовий лоадер: дві кільцеві анімації навколо літери "S", опціональний підпис.
position: absolute + z-index: 20000 — центрується відносно найближчого позиціонованого батька.
<!-- В позиціонованому контейнері -->
<div style="position: relative; min-height: 300px;">
<SkyLoader text="Завантаження..." />
</div>
<!-- Без підпису -->
<SkyLoader />
Props
| Prop | Тип | Обов'язково | Опис |
|---|---|---|---|
text |
String |
ні | Підпис під лоадером |
SkyCardRow
Рядок "label + value" (або довільний контент через slot).
<SkyCardRow label="Store ID" value="12345" />
<!-- зі своїм вмістом праворуч -->
<SkyCardRow label="Статус">
<SkyBadge tone="success" label="Під'єднано" />
</SkyCardRow>
Props
| Prop | Тип | Обов'язково | Опис |
|---|---|---|---|
label |
String |
так | Підпис зліва |
value |
String | Number |
ні | Значення справа (ігнорується якщо є slot) |
Slots
| Slot | Опис |
|---|---|
default |
Override правої колонки |
SkyTileCard
Тайл-картка: зверху — іконка + заголовок + підзаголовок; знизу — довільний вміст через default slot. Підходить для списків інтеграцій / додатків / швидкого доступу.
<SkyTileCard
title="Glovo"
subtitle="Інтеграція з сервісом доставки"
image-url="/image/glovo_logo.png"
/>
<!-- з додатковим контентом знизу -->
<SkyTileCard title="Glovo" subtitle="Інтеграція" image-url="/image/glovo_logo.png">
<SkyBadge tone="success" label="Активно" />
<SkyButton variant="outline" block>Налаштувати</SkyButton>
</SkyTileCard>
<!-- зі своєю іконкою -->
<SkyTileCard title="Налаштування">
<template #icon>
<SettingsIcon />
</template>
</SkyTileCard>
Props
| Prop | Тип | Обов'язково | Опис |
|---|---|---|---|
title |
String |
так | Заголовок |
subtitle |
String |
ні | Підзаголовок |
imageUrl |
String |
ні | URL зображення |
Slots
| Slot | Опис |
|---|---|
icon |
Override іконки |
default |
Додатковий вміст під header (бейджі, кнопки тощо) |
CSS змінні
--sky-tile-bg: #fff
--sky-tile-radius: 12px
--sky-tile-shadow: 0 1px 12px rgba(0,0,0,0.08)
--sky-tile-icon-size: 56px
--sky-tile-icon-radius: 12px
--sky-tile-icon-bg: #00c279
--sky-tile-icon-color: #fff
--sky-tile-title-color: #1a1a1a
--sky-tile-subtitle-color: #666
SkyDataTable
Грід на TanStack Table v9: headless-ядро тримає стан і обчислення, розмітка й стилі — наші. Візуально це та сама таблиця, що в POS. Повний опис — у документації.
<script setup lang="ts">
import { h } from 'vue'
import { SkyDataTable, SkyBadge, createSkyColumnHelper } from '@skyservice-developers/vue-dev-kit'
const column = createSkyColumnHelper<Product>()
const columns = [
column.accessor('name', { header: 'Назва', meta: { widthFr: 2 } }),
column.accessor('price', {
header: 'Ціна',
size: 110,
meta: { align: 'right' },
cell: ({ getValue }) => `${getValue()} ₴`,
}),
column.accessor('status', {
header: 'Статус',
size: 150,
enableSorting: false,
cell: ({ getValue }) =>
h(SkyBadge, { tone: getValue() === 'Активний' ? 'success' : 'default', label: getValue() }),
}),
]
</script>
<template>
<SkyDataTable :columns="columns" :data="products" :page-size="25" row-id="id" searchable />
</template>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
columns |
ColumnDef[] |
— | Опис колонок (TanStack) |
data |
TData[] |
— | Дані |
features |
TableFeatures |
skyTableFeatures |
Власний набір фіч |
rowId |
String | (row) => string |
— | Ключ рядка |
pageSize |
Number |
0 |
Розмір сторінки; 0 — без пагінації |
searchable |
Boolean |
false |
Поле глобального пошуку в тулбарі |
initialSorting |
SortingState |
— | Початкове сортування |
emptyText |
String |
'Даних немає' |
Текст порожнього стану |
interactiveRows |
Boolean |
false |
Курсор і hover на рядках |
virtual |
Boolean | SkyDataTableVirtualOptions |
false |
Віртуалізація тіла — у DOM лише видиме вікно рядків |
Слоти: toolbar, actions, footer, empty (усі отримують { table }). Події: row-click, reach-end (тільки у віртуальному режимі). Інстанс таблиці доступний через ref → tableRef.table.
Віртуалізація
На кілька тисяч рядків браузер провисає не на даних, а на DOM. Проп virtual лишає в розмітці тільки видиме вікно:
<SkyDataTable :columns="columns" :data="rows" row-id="id" virtual />
<SkyDataTable
:columns="columns"
:data="rows"
row-id="id"
:virtual="{ estimateSize: 44, overscan: 8 }"
@reach-end="loadMore"
/>
| Опція | Тип | За замовчуванням | Опис |
|---|---|---|---|
estimateSize |
Number |
40 |
Очікувана висота рядка в px |
overscan |
Number |
6 |
Скільки рядків тримати понад видиме вікно |
dynamic |
Boolean |
false |
Рядки різної висоти — вимірювання через ResizeObserver |
Змінюється рівно одне: хто вирішує, які рядки зараз у DOM. Сортування, фільтрація, вибір і getRowCount() працюють по всьому набору даних.
Потрібна обмежена висота. Скрол-контейнером є тіло таблиці — дайте таблиці
heightабоflex: 1; min-height: 0, інакше скролитиметься сторінка й вікно не зсуватиметься.
Не поєднуйте з
pageSize. На сторінці в кілька десятків рядків віртуалізувати нічого; компонент попередить у консолі. Природна пара доvirtual— довантаження заreach-end.
Набір фіч
Фічі TanStack v9 вмикаються явно — кіт експортує стабільний skyTableFeatures (сортування, вибір, видимість колонок, пагінація, фільтрація + слоти row-моделей і реєстри sortFns/filterFns). Потрібно більше — зберіть свій обʼєкт і передайте пропом features:
import { tableFeatures, rowExpandingFeature, createExpandedRowModel } from '@tanstack/vue-table'
import { skyTableFeatures } from '@skyservice-developers/vue-dev-kit'
const features = tableFeatures({
...skyTableFeatures,
rowExpandingFeature,
expandedRowModel: createExpandedRowModel(),
})
Ширина колонок: size: 150 — фіксовані px, meta: { widthFr: 2 } — гнучка частка (має пріоритет над size), meta: { align } і meta: { minWidthScreen } — вирівнювання й приховування на вузьких екранах.
@tanstack/vue-tableвходить у залежності пакета й лишається зовнішнім у бандлі — застосунки, які гріда не використовують, його не тягнуть.
Таблиця з примітивів (композиційна)
Набір примітивів і композаблів замість одного великого компонента: береш тільки те, що треба. Без вибору рядків, без масових дій, без віртуалізації — і нічого з цього не потрапляє в бандл. Повний опис — у документації.
<script setup>
import {
SkyTableRoot, SkyTableHeader, SkyTableHead,
SkyTableBody, SkyTableVirtualBody, SkyTableRow, SkyTableCell,
} from '@skyservice-developers/vue-dev-kit'
const columns = [
{ name: 'name', title: 'Товар', widthFr: 2 },
{ name: 'price', title: 'Ціна', width: 90, align: 'right' },
]
</script>
<template>
<SkyTableRoot :columns="columns">
<SkyTableHeader>
<SkyTableHead v-for="c in columns" :key="c.name" :column="c" />
</SkyTableHeader>
<SkyTableBody>
<SkyTableRow v-for="row in rows" :key="row.id">
<SkyTableCell>{{ row.name }}</SkyTableCell>
<SkyTableCell align="right">{{ row.price }}</SkyTableCell>
</SkyTableRow>
</SkyTableBody>
</SkyTableRoot>
</template>
Що входить
| Шар | Експорти | Коли підключати |
|---|---|---|
Примітиви (shared/ui/table) |
SkyTableRoot, SkyTableHeader, SkyTableHead, SkyTableBody, SkyTableVirtualBody, SkyTableRow, SkyTableCell, SkyTableEmpty |
завжди |
Композабли (shared/lib/table) |
useTableSort, useTableSelection, useColumnVisibility |
за потреби |
Фічі (features/table) |
TableMassActions, TableColumnSettings, TableVirtualBody |
опційно, кожна окремо |
const { sort, toggle } = useTableSort({ initial: { of: 'name', ot: 'asc' } })
const { selected, isSelected, toggle: select, toggleAll } = useTableSelection(rows, { rowId: 'id' })
const { visibility, visibleColumns } = useColumnVisibility(columns, { storageKey: 'products' })
Композабли не сортують і не фільтрують дані — вони тримають стан, а дані лишаються за застосунком (зазвичай сортує сервер). Колонка вибору описується як звичайна колонка ({ name: '_select', width: 44 }), щоб грід шапки й рядків рахувався з одного джерела.
Тіло: два режими, один контракт
SkyTableBody без пропа rows — прозора обгортка (рядки складаєте самі, як вище). З rows — ітерує сам і віддає scoped-слот:
<SkyTableBody :rows="rows" :row-key="row => row.id">
<template #empty><SkyTableEmpty text="Даних немає" /></template>
<template #default="{ row }">
<SkyTableRow><SkyTableCell>{{ row.name }}</SkyTableCell></SkyTableRow>
</template>
</SkyTableBody>
SkyTableVirtualBody приймає ті самі rows/rowKey і віддає той самий слот, лишаючи в DOM тільки видиме вікно рядків. Тому перемикання віртуалізації — це заміна тега, а не переписування розмітки:
<component :is="virtual ? SkyTableVirtualBody : SkyTableBody" :rows="rows" :row-key="row => row.id">
<template #default="{ row }">…</template>
</component>
Prop SkyTableVirtualBody |
Тип | За замовчуванням | Опис |
|---|---|---|---|
rows |
TRow[] |
— | Рядки (обовʼязково) |
rowKey |
(row, index) => string | number |
індекс | Ключ рядка |
estimateSize |
Number |
40 |
Очікувана висота рядка в px |
overscan |
Number |
6 |
Скільки рядків тримати понад видиме вікно |
dynamic |
Boolean |
false |
Рядки різної висоти — вимірювання через ResizeObserver |
Подія reach-end — коли останній рядок увійшов у вікно.
Потрібна обмежена висота. Скрол-контейнером є саме тіло. Якщо таблиця росте разом з контентом і скролиться сторінка, вікно ніколи не зсувається. Дайте таблиці
heightабоflex: 1; min-height: 0.
TableVirtualBodyзfeatures/tableлишається як сумісна обгортка зі старим контрактом (items+ слот{ item, index }). У новому коді берітьSkyTableVirtualBody.
SkyTable
Віртуал-скрол таблиця (на @tanstack/vue-virtual) для великих списків — у DOM тримаються лише видимі рядки. Розмітка й стилі 1:1 з таблицею товарів POS: чекбокси, масові дії, сортування, зміна ширини/видимості колонок, розкриття вкладених рядків (модифікацій), inline-редагування комірок і теги.
<SkyTable
:params="params"
:json="json"
:main-json-data="items"
@getData="loadData"
@updateSelected="onSelected"
@open="onOpenRow"
/>
const items = [
{ id: 1, name: 'Кава', price: 45, status: 'Активний' },
// ...
]
const params = reactive({
id: 'id', // ім'я ключового поля рядка
name: 'products', // назва інстансу таблиці
selected: [], // обрані рядки (мутується всередині)
allSelect: false,
footer: false, // показати футер-панель
massActions: { delete: { value: 'delete', title: 'Видалити' } },
sort: { of: '', ot: '' }, // of — поле, ot — напрям
header: [
{ title: 'Назва', name: 'name', sort: 'name', width: 240, enable: true },
{ title: 'Ціна', name: 'price', sort: 'price', width: 130, enable: true, customItemComponent: 'itemInput' },
],
})
const json = { items, total: items.length }
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
params |
Object |
{} |
Конфіг таблиці (див. нижче) |
json |
{ items: any[]; total: number } |
{} |
Дані: items + загальна кількість total |
mainJsonData |
Array |
[] |
Масив рядків для рендеру (те, що бачить віртуал-скрол) |
justDeleted |
String |
'' |
id щойно видаленого рядка — прибирає його з selected |
params — конфіг таблиці
| Поле | Тип | Опис |
|---|---|---|
id |
String |
Ім'я ключового поля рядка (напр. 'id') |
name |
String |
Назва інстансу таблиці |
selected |
Array |
Обрані рядки (мутується компонентом) |
allSelect |
Boolean |
Чи обрано всі |
footer |
Boolean |
Показати футер-панель |
massActions |
Record<string, { value: string; title: string }> |
Масові дії; порожньо або { none: … } — приховати панель |
sort |
{ of: string; ot: string } |
Поточне сортування: of — поле, ot — напрям |
header |
Column[] |
Опис колонок (див. нижче) |
header[] — колонка
| Поле | Тип | Опис |
|---|---|---|
title |
String |
Заголовок колонки |
name |
String |
Ключ у рядку даних |
sort |
String | false |
Поле сортування або false |
width |
Number |
Фіксована ширина, px |
widthFr |
Number |
Гнучка ширина (flex-частка), альтернатива width |
enable |
Boolean |
Видимість колонки за замовчуванням |
customItemComponent |
'itemInput' | 'itemSelect' | 'itemTags' | 'itemTagEditor' |
Тип комірки; без нього — звичайний текст |
minWidthScreen |
Number |
Ховати колонку на екранах, вужчих за це значення |
Events
| Event | Payload | Опис |
|---|---|---|
getData |
{ of, ot } |
Запит даних (зміна сортування) |
updateSelected |
any[] |
Змінився список обраних рядків |
updateMassactionData |
{ action, items } |
Застосовано масову дію |
open |
(item, isModification) |
Клік по рядку |
openContext |
(item, { x, y }) |
Контекстне меню (right-click / long-tap) |
inputEdit |
(payload, columnName, handlers, item) |
Inline-редагування комірки itemInput |
selectUpdate |
(columnName, item) |
Зміна в комірці itemSelect |
openTagsModal |
item |
Відкрити модалку тегів (itemTags) |
deleteTag |
(item, tag) |
Видалити тег |
deleteRow |
item |
Видалити рядок (кнопка при params.isShowDeleteRow) |
Slots
| Slot | Props | Опис |
|---|---|---|
cell-<name> |
{ row, value, item } |
Кастомний рендер комірки колонки <name> |
Примітка: компонент розрахований на хост-середовище Skyservice POS. Він читає словник i18n з
window.lang(з UA-фолбеками) і використовує host-assets за абсолютними шляхами (/image/dragons/…,/svg/arrow_black.svg), які віддає застосунок-хост — тому в бібліотеці вони навмисно не бандляться (vite.config→transformAssetUrls.includeAbsolute: false).@tanstack/vue-virtual— зовнішня залежність: встанови її в застосунку-хості.
FunctionalCalendar
Перенесено зі SkyMarket (src/lib/functional-calendar), стилі й розмітка 1:1. Форк vue-functional-calendar під Vue 3 — пропси, події та слоти без змін відносно оригіналу.
<FunctionalCalendar
:value="calendarData"
@input="calendarData = $event"
is-date-range
is-modal
/>
SkyDateRangePicker
Перенесено зі SkyMarket (DatePickerRange.vue) — готовий попап-піка діапазону дат на базі FunctionalCalendar: іконка-тригер, лейбл періоду, пресети (Сьогодні / Вчора / Тиждень / Місяць / Минулий-Поточний місяць / За весь час), стилі 1:1. На відміну від оригіналу, який читав store/@/langs конкретного застосунку, тут стан і тексти (з UA-фолбеками) локальні.
<SkyDateRangePicker v-model="range" @set-date="onPreset" />
const range = reactive({ start: '', end: '' }) // { start, end } у форматі yyyy-mm-dd
Features
SkyCheckboxFilter
Кнопка-фільтр з дропдауном, пошуком і мульти-вибором (через SkyCheckbox). Оболонку — тригер, панель, позиціювання, закриття — тримає SkyFilterDropdown. Стилі повторюють адмінку Skymarket 1:1.
<SkyCheckboxFilter
v-model="selectedCategories"
title="Категорії"
:options="[
{ value: 'alc', name: 'Алкоголь' },
{ value: 'food', name: 'Їжа' },
]"
/>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
title |
String |
— | Заголовок фільтра (показується у тригері) |
options |
Array<{ value, name }> |
[] |
Опції для вибору |
modelValue |
Array<String|Number> |
[] |
Вибрані значення (v-model) |
selectAllLabel |
String |
'Обрати все' |
Лейбл кнопки "Обрати все" |
clearLabel |
String |
'Очистити' |
Лейбл кнопки "Очистити" |
doneLabel |
String |
'Готово' |
Лейбл кнопки "Готово" |
searchPlaceholder |
String |
'Пошук' |
Placeholder пошуку |
selectAll |
Boolean |
true |
Показувати «Обрати все» |
searchable |
Boolean |
true |
Показувати поле пошуку |
disabled |
Boolean |
false |
Вимкнений стан |
SkySelectFilter
Той самий фільтр-чіп, але з одиничним вибором — для випадків, коли бекенд приймає рівно одне значення. Після вибору дропдаун закривається.
<SkySelectFilter
v-model="categoryId"
title="Категорія"
all-label="Усі категорії"
:options="[
{ value: 10, name: 'Напої', depth: 0 },
{ value: 11, name: 'Кава', depth: 1 },
]"
/>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
title |
String |
— | Заголовок фільтра (показується у тригері) |
options |
Array<{ value, name, depth? }> |
— | Опції; depth лише відступає рядок |
modelValue |
String|Number|null |
null |
Обране значення (v-model); null — фільтр вимкнено |
allLabel |
String |
'' |
Лейбл рядка «без фільтра»; порожній — рядка немає |
searchPlaceholder |
String |
'Пошук' |
Placeholder пошуку |
emptyLabel |
String |
'Нічого не знайдено' |
Текст порожнього результату пошуку |
searchable |
Boolean |
true |
Показувати поле пошуку |
disabled |
Boolean |
false |
Вимкнений стан |
SkyPagination
Посторінкова навігація в стилі таблиць адмінки: селектор кількості на сторінці, далі номери зі згортанням у «…». Нічого не завантажує — лише повідомляє, що обрали.
<SkyPagination
:total="total"
:page="page"
:page-size="pageSize"
:all-label="(n) => `Усі ${n}`"
@update:page="page = $event; reload()"
@update:page-size="pageSize = $event; page = 1; reload()"
/>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
total |
Number |
— | Скільки всього записів у вибірці, не на сторінці |
page |
Number |
— | Поточна сторінка, з одиниці |
pageSize |
Number |
— | Записів на сторінці |
pageSizeOptions |
Number[] |
[5,10,25,50,100,200,500,750,1000] |
Варіанти селектора; лишаються менші за total |
allLabel |
(total) => String |
— | Підпис пункту «показати все»; без нього пункту немає |
allLimit |
Number |
1000 |
До якої кількості пропонувати «показати все» |
siblings |
Number |
1 |
Скільки сусідніх сторінок показувати обабіч поточної |
disabled |
Boolean |
false |
Вимкнений стан |
Events: update:page, update:pageSize. CSS-змінні — --sky-pagination-*.
Потрібен total. Для курсорних джерел, де загальна кількість невідома, а вперед ведуть
лише курсори, номери сторінок неможливі — там потрібна навігація «назад / далі».
SkyFilterDropdown
Оболонка фільтра: тригер-чіп + панель. Бере на себе тільки спільне — стан відкриття,
позиціювання, закриття і доступність; вміст панелі за викликачем. На ній побудовані
SkyCheckboxFilter і SkySelectFilter; бери її напряму для фільтра з власним вмістом.
Панель телепортується у <body> (щоб transform у предка не ламав position: fixed),
перепозиціюється на скрол і resize, притискається до країв екрана й розкривається вгору,
коли знизу тісно. Скрол сторінки не локиться. Закриття — клік поза межами, Esc.
<SkyFilterDropdown title="Період" summary="Цей тиждень">
<template #default="{ close }">
<button @click="close">Готово</button>
</template>
</SkyFilterDropdown>
Props
| Prop | Тип | За замовчуванням | Опис |
|---|---|---|---|
title |
String |
— | Лейбл чіпа |
summary |
String |
'' |
Заміняє title, коли обрано рівно одне |
badge |
String|Number |
'' |
Показується поруч із title (напр. лічильник) |
align |
'start'|'end' |
'start' |
До якого краю тригера притискається панель |
width |
Number |
280 |
Ширина панелі в px |
disabled |
Boolean |
false |
Вимкнений стан |
Slots / Events / Expose
slot default |
{ close } — вміст панелі |
| events | open, close |
| expose | open(), close(), toggle(), isOpen |
CSS-змінні (--sky-filter-*) — див. доки.
formatMoney — сума зі значком валюти
Живе в SDK (@skyservice-developers/vue-dev-kit/sdk), а не серед компонентів: там
немає Vue, тож модуль доступний і адмінці на Vue 2.
import { formatMoney, currencyIcon } from '@skyservice-developers/vue-dev-kit/sdk'
formatMoney('110', { currency: 'UAH' }) // 110.00 ₴
formatMoney('1250', { currency: 'UAH' }) // 1 250.00 ₴
formatMoney('110', { currency: 'USD', locale: 'en' }) // $110.00
formatMoney('110', { currency: null }) // 110.00
currencyIcon('KZT') // ₸
| Опція | Типово | Опис |
|---|---|---|
currency |
null |
ISO валюти. Без нього повертається лише число |
locale |
'uk' |
Мова інтерфейсу — вирішує, з якого боку значок |
decimals |
2 |
Знаків після коми |
Де стоїть значок — за правилами мови, не валюти: українська, польська й німецька
ставлять його після числа, англійська й турецька перед. Позицію питаємо в Intl, а сам
значок беремо зі свого списку: для більшості наших валют Intl віддає ISO-код замість
символу (перевірено на всіх 55 — ₸, ₺, zł, ₫ він не знає).
Формат числа зведений з адмінкою (CurrencyFormatted + formatNumberWithSpaces): дві
копійки, тисячі через пробіл, крапка як десятковий роздільник. Локальні розділювачі
свідомо не беремо — інакше та сама сума виглядала б по-різному в адмінці й у міні-аппі.
Пробіли всередині — нерозривні, щоб ціна не переносилась на два рядки посеред числа.
Нечислове значення повертається як є: краще показати сире, ніж NaN.
sky-service-ui-components
Бібліотека re-export-ує весь публічний API пакета sky-service-ui-components. Окремо встановлювати його не потрібно — він вже входить у залежності vue-dev-kit.
NotificationElement (Web Component)
Кастомний HTML-елемент для відображення тостів. Реєструється автоматично при імпорті.
import { NotificationElement } from '@skyservice-developers/vue-dev-kit'
Додай елемент в index.html або кореневий компонент:
<notification-element id="my-notifications"></notification-element>
notificationModule
Програмний API для показу тостів.
import { notificationModule } from '@skyservice-developers/vue-dev-kit'
// Типи: default | success | error | warning | info | loading
await notificationModule.notify.success({ message: 'Збережено' })
await notificationModule.notify.error({ message: 'Помилка' })
await notificationModule.notify.warning({ message: 'Увага' })
await notificationModule.notify.info({ message: 'Підказка' })
const id = await notificationModule.notify.loading({ message: 'Завантаження...' })
// Прибрати конкретний тост
await notificationModule.notify.dismiss(id)
Метод повертає Promise<string | number | null> — id тосту або null при помилці.
globalStore
Глобальний стор бібліотеки (Pinia store). Використовується внутрішньо notificationModule.
import { globalStore } from '@skyservice-developers/vue-dev-kit'
const store = globalStore()
Теміzація
Всі компоненти підтримують кастомізацію через CSS змінні. Перевизначайте їх глобально або локально:
/* Глобально */
:root {
--sky-btn-primary-bg: #6366f1;
--sky-btn-radius: 8px;
--sky-select-radius: 8px;
--sky-modal-z-index: 1000;
}
/* Локально для конкретного блоку */
.my-form {
--sky-btn-padding: 12px 16px;
--sky-select-padding: 8px 12px;
}
Виріз екрана (safe area)
Бібліотека сама додає body { padding-left/right: env(safe-area-inset-left/right) }
(src/styles/safe-area.css), щоб у горизонтальній орієнтації вміст не заходив під камеру.
Щоб це працювало на iOS, у index.html застосунку має бути
<meta name="viewport" content="..., viewport-fit=cover">. Оверлеї модалок мають
position: fixed, тож їхній фон лишається на весь екран. Фіксовані елементи застосунку
(шапка, нижня панель) відступ body не отримують — додайте їм left/right з env() самі.
Якщо застосунок уже сам відступає body від вирізу, подвійний відступ прибирається на його боці.
Іконки
SVG-іконки лежать у src/shared/assets/icons/. Збірка через vite-svg-loader — за замовчуванням повертає URL, опціонально можна імпортувати як Vue-компонент або сирий рядок:
// URL — для <img :src> (default)
import iconUrl from '@/shared/assets/icons/home.svg'
// Vue-компонент — для inline SVG, стилізації через currentColor
import IconHome from '@/shared/assets/icons/home.svg?component'
// Сирий вміст SVG
import iconRaw from '@/shared/assets/icons/home.svg?raw'
Типи для query-параметрів задекларовані в src/vue-shim.d.ts.
Розробка
npm install
# Документація з живими демо (localhost:5173)
npm run docs:dev
# Білд (компоненти + SDK → dist/)
npm run build
# Типи
npm run typecheck
Реліз
Публікація на npm автоматична — пуш у main запускає GitHub Action
Publish to npm, який збирає й публікує пакет. Руками npm publish не запускати.
| Що треба | Що зробити |
|---|---|
| Випустити нову версію | Бампнути version у package.json → змержити в main |
| Залити зміни без релізу | Не чіпати version — публікація пропуститься, CI лишиться зеленим |
| Опублікувати вручну | Actions → Publish to npm → Run workflow |
Публікація пропускається, якщо версія з package.json уже є в реєстрі. Пуші, що
зачіпають лише **.md або .github/**, воркфлоу не запускають — для них
є ручний запуск.
Поки версію не бампнули, споживачі змін не бачать: npm далі віддає попередню.
Структура проекту
Організовано за Feature-Sliced Design:
src/
├── index.ts # публічний API (shared/ui + features + sdk)
├── langs/ # словники i18n (fallback для window.lang, використовує SkyTable)
├── components/ # vendored UI-примітиви (shadcn tags-input) для SkyTable
├── shared/
│ ├── assets/
│ │ └── icons/ # SVG-іконки (vite-svg-loader)
│ └── ui/ # базові UI-компоненти
│ ├── index.ts
│ ├── Header/
│ ├── Modal/
│ ├── Dialog/ DialogModal/ DialogNext/
│ ├── BaseTeleport/
│ ├── SkyButton/
│ ├── SkySelect/ SkySelectSearch/ SkyInput/ SkySearchInput/ SkyCheckbox/
│ ├── SkyTabs/ # сегментовані таби з анімованим індикатором
│ ├── SkyAlert/
│ ├── SkyBadge/
│ ├── SkyCard/ SkyCardHeader/ SkyCardRow/
│ ├── SkyStatCard/ # інфо-картка зі статистикою (іконка + заголовок + значення, може бути клікабельною)
│ ├── SkyPageHeader/ # заголовок сторінки: title + description + slot
│ ├── SkyLoader/
│ ├── table/ # примітиви композиційної таблиці (Root/Header/Head/Body/VirtualBody/Row/Cell/Empty)
│ ├── SkyTable/ # готова віртуал-скрол таблиця (Header/Row/Footer/DynamicScroller/items/*)
│ ├── SkyTileCard/
│ ├── SkyFilterDropdown/ # оболонка фільтра (тригер + панель) + FilterSearch
│ ├── SkyPagination/ # посторінкова навігація зі селектором кількості
│ ├── functional-calendar/ # форк vue-functional-calendar, перенесений зі SkyMarket 1:1
│ ├── SkyDateRangePicker/ # DatePickerRange зі SkyMarket, store/langs → props/локальний стан
│ └── <Component>/
│ ├── <Component>.vue
│ └── index.ts
│ └── lib/
│ └── table/ # skyTableFeatures + useTableSort / useTableSelection / useColumnVisibility
├── features/ # фічові блоки (orchestration shared/ui)
│ ├── index.ts
│ ├── SkyCheckboxFilter/ # мульти-вибір на SkyFilterDropdown
│ ├── SkySelectFilter/ # одиничний вибір на SkyFilterDropdown
│ ├── table/ # TableMassActions / TableColumnSettings / TableVirtualBody
│ └── data-table/ # SkyDataTable на TanStack v9 + перемикач колонок
├── sdk/ # TypeScript SDK (без Vue залежностей)
│ ├── bridge.ts # iframe postMessage API
│ ├── api.ts # SkyserviceAPI HTTP клієнт
│ ├── webview.ts # детекція webview/iframe
│ ├── types.ts # DTO типи (Tradepoint, Category, Product, AppIntegration, PermsMap)
│ └── index.ts
└── styles/
Ліцензія
MIT © Skyservice-POS
Related Packages
Version History
| Version | Released | License |
|---|---|---|
| 3.5.0 | MIT | |
| 3.4.2 | MIT | |
| 3.4.1 | MIT | |
| 3.4.0 | MIT | |
| 3.3.3 | MIT | |
| 3.3.2 | MIT | |
| 3.3.1 | MIT | |
| 3.3.0 | MIT | |
| 3.2.0 | MIT | |
| 3.1.2 | MIT | |
| 3.1.1 | MIT | |
| 3.1.0 | MIT | |
| 3.0.0 | MIT | |
| 2.22.0 | MIT | |
| 2.21.1 | MIT | |
| 2.21.0 | MIT | |
| 2.20.0 | MIT | |
| 2.19.4 | MIT | |
| 2.19.3 | MIT | |
| 2.19.2 | MIT | |
| 2.19.1 | MIT | |
| 2.19.0 | MIT | |
| 2.18.0 | MIT | |
| 2.17.2 | MIT | |
| 2.17.1 | MIT | |
| 2.17.0 | MIT | |
| 2.16.1 | MIT | |
| 2.16.0 | MIT | |
| 2.15.0 | MIT | |
| 2.14.4 | MIT |
Showing the latest 30 of 121. See every release on npm