Tham chiếu

Component giao diện

Danh mục element pos-* dùng trong extension Worker UI: thuộc tính, sự kiện, quy tắc.

Xem Markdown

Áp dụng cho api_version: "2026-01".

Extension Worker UI (block, action modal/fullscreen, trang render: "worker") chỉ dựng giao diện bằng các element pos-* dưới đây. POS vẽ mỗi element bằng component thật của POS, nên giao diện luôn đồng bộ với POS.

Trang iframe không dùng danh sách này; dùng HTML/React tự do, có thể kèm @pancake-pos/ui để giao diện giống POS.

Quy tắc chung

  • Chỉ dùng element trong danh sách. Thẻ HTML thường (div, span, img, table, script…) và element lạ không được vẽ; extension báo lỗi trong nhật ký của app.
  • Text chỉ hiển thị khi nằm trong pos-text, pos-heading, pos-badge, pos-link, pos-button, pos-banner.
  • Không có style, className, HTML thô. Màu sắc, khoảng cách, font theo POS.
  • URL (href, src) chỉ chấp nhận https://.
  • Sự kiện truyền giá trị qua event.detail. Trong JSX dùng thuộc tính onClick, onChange… như component thông thường. Với JS thuần, tên sự kiện là phần sau on viết thường: onPageChange → addEventListener('pagechange', …).
  • Giới hạn 2.000 element mỗi extension; pos-table tối đa 500 dòng mỗi lần render (dùng phân trang).
  • Thuộc tính dạng mảng/đối tượng (options, columns, rows, pagination) truyền trực tiếp bằng JSX, không cần JSON.stringify.
TSX
<pos-stack gap="small">
  <pos-text-field label="Mã phiếu" value={code} onChange={(e) => setCode(e.detail)} />
  <pos-button variant="primary" loading={saving} onClick={save}>Lưu</pos-button>
</pos-stack>

Không dùng framework thì dựng bằng DOM API của runtime (xem Môi trường worker):

JavaScript
let code = '';

const field = document.createElement('pos-text-field');
field.label = 'Mã phiếu';
field.addEventListener('change', (e) => { code = e.detail; });

const button = document.createElement('pos-button');
button.variant = 'primary';
button.textContent = 'Lưu';
button.addEventListener('click', () => save(code));

const stack = document.createElement('pos-stack');
stack.gap = 'small';
stack.append(field, button);
document.body.append(stack);

Bố cục

pos-stack

Xếp các phần tử con theo hàng dọc hoặc ngang.

Thuộc tính Kiểu Mặc định Mô tả
direction 'vertical' | 'horizontal' 'vertical' Hướng xếp
gap 'none' | 'small' | 'medium' | 'large' 'medium' Khoảng cách giữa phần tử
align 'start' | 'center' | 'end' | 'stretch' 'stretch' Căn theo trục phụ
justify 'start' | 'center' | 'end' | 'space-between' 'start' Căn theo trục chính
wrap boolean false Cho xuống dòng (khi horizontal)

pos-grid

Lưới cột đều nhau; trên màn hình hẹp tự chuyển về một cột.

Thuộc tính Kiểu Mặc định Mô tả
columns 1–6 2 Số cột
gap 'none' | 'small' | 'medium' | 'large' 'medium' Khoảng cách

pos-section

Khung có tiêu đề, dùng làm vỏ ngoài của block.

Thuộc tính Kiểu Mô tả
heading string Tiêu đề
description string Mô tả ngắn dưới tiêu đề

Slot actions: nút hiển thị ở góc phải tiêu đề.

TSX
<pos-section heading="Công nợ">
  <pos-button slot="actions" variant="tertiary" onClick={reload}>Tải lại</pos-button>
  <pos-text>...</pos-text>
</pos-section>

pos-divider

Đường kẻ ngang. Không có thuộc tính.

Hiển thị

pos-text

Thuộc tính Kiểu Mặc định
tone 'default' | 'subdued' | 'success' | 'warning' | 'critical' 'default'
size 'small' | 'medium' | 'large' 'medium'
weight 'regular' | 'semibold' | 'bold' 'regular'

pos-heading

Thuộc tính Kiểu Mặc định
level 1–4 3

pos-badge

Nhãn trạng thái ngắn.

Thuộc tính Kiểu Mặc định
tone 'default' | 'info' | 'success' | 'warning' | 'critical' 'default'

pos-money

Hiển thị số tiền theo định dạng và đơn vị tiền tệ của shop.

Thuộc tính Kiểu Mô tả
value number Số tiền
currency string Mã tiền tệ ISO 4217; mặc định là tiền tệ của shop
tone như pos-text Màu chữ
Thuộc tính Kiểu Mô tả
href string URL https://, luôn mở tab mới

Sự kiện onClick: dùng khi link thực hiện thao tác trong app thay vì mở URL (không đặt href).

pos-image

Thuộc tính Kiểu Mặc định Mô tả
src string URL https:// thuộc domain trong network.allowed_origins, hoặc public/<tên file> trỏ tới file trong thư mục public/ của dự án
alt string Văn bản thay thế
width, height number Kích thước (px)
fit 'cover' | 'contain' 'cover' Cách co ảnh

POS tải ảnh qua máy chủ trung gian của sandbox: chỉ nhận định dạng ảnh, tối đa 5 MB. Ảnh từ domain chưa khai không hiển thị (chế độ phát triển có cảnh báo).

Nhập liệu

Thuộc tính chung của mọi ô nhập:

Thuộc tính Kiểu Mô tả
label string Nhãn
value tuỳ component Giá trị hiện tại (component có kiểm soát)
disabled boolean Khoá ô
required boolean Hiện dấu bắt buộc (không tự kiểm tra)
error string Thông báo lỗi dưới ô
help string Gợi ý dưới ô

Sự kiện chung: onChange (event.detail là giá trị mới), onBlur.

POS hiện ngay chữ người dùng gõ rồi mới gửi onChange cho app, nên ô không giật dù app đang bận. Nếu app đặt value khác giá trị người dùng vừa nhập (ví dụ chuẩn hoá chữ hoa hoặc xóa ô), POS hiển thị giá trị của app. Nên đặt value ngay trong onChange; đổi value sau một lệnh await có thể ghi đè chữ người dùng đang gõ.

pos-text-field

Thuộc tính Kiểu Mô tả
value string
placeholder string
multiline boolean | number Ô nhiều dòng; số là số dòng hiển thị
maxLength number Giới hạn ký tự

pos-number-field

Thuộc tính Kiểu Mô tả
value number | null
min, max, step number
suffix string Đơn vị hiển thị sau số, ví dụ "%"

pos-select

Thuộc tính Kiểu Mô tả
options { label: string; value: string; disabled?: boolean }[] Danh sách lựa chọn
value string | string[] Mảng khi multiple
multiple boolean Chọn nhiều
placeholder string
searchable boolean Cho gõ tìm trong danh sách

pos-checkbox

Thuộc tính Kiểu Mô tả
checked boolean
label string

onChange: event.detail là boolean.

pos-date-picker

Thuộc tính Kiểu Mô tả
value string | null Ngày dạng YYYY-MM-DD
min, max string Giới hạn ngày chọn, dạng YYYY-MM-DD

Phản hồi

pos-banner

Thông báo nổi bật trong extension.

Thuộc tính Kiểu Mặc định Mô tả
tone 'info' | 'success' | 'warning' | 'critical' 'info'
heading string Tiêu đề
dismissible boolean false Hiện nút đóng

Sự kiện onDismiss.

pos-spinner

Thuộc tính Kiểu Mặc định
size 'small' | 'medium' | 'large' 'medium'

Dữ liệu và thao tác

pos-button

Thuộc tính Kiểu Mặc định Mô tả
variant 'primary' | 'secondary' | 'tertiary' | 'danger' 'secondary' Kiểu nút
size 'small' | 'medium' 'medium'
loading boolean false Hiện trạng thái đang xử lý, chặn bấm
disabled boolean false

Sự kiện onClick.

pos-table

Bảng dữ liệu. Ô chỉ chứa giá trị (chuỗi, số); POS định dạng theo format của cột.

Thuộc tính Kiểu Mặc định Mô tả
columns Column[] Định nghĩa cột
rows object[] [] Dữ liệu, tối đa 500 dòng
rowKey string 'id' Trường dùng làm khoá dòng
loading boolean false
pagination { page: number; pageSize: number; total: number } | null null Phân trang phía app
emptyText string Nội dung khi không có dòng
TypeScript
type Column = {
  key: string;                       // tên trường trong row
  title: string;
  align?: 'left' | 'center' | 'right';
  width?: number;                    // px
  format?: 'text' | 'number' | 'money' | 'date' | 'datetime';
};

Sự kiện:

  • onPageChange: event.detail là { page, pageSize }.
  • onRowClick: event.detail là row được bấm.
TSX
<pos-table
  columns={[
    { key: 'system_id', title: 'Mã đơn' },
    { key: 'total_price', title: 'Tổng tiền', format: 'money', align: 'right' },
  ]}
  rows={orders}
  rowKey="system_id"
  pagination={{ page, pageSize: 20, total }}
  onPageChange={(e) => setPage(e.detail.page)}
/>

pos-modal

Hộp thoại mở từ bên trong extension (ví dụ từ một nút trong block).

Thuộc tính Kiểu Mô tả
open boolean Trạng thái mở
heading string Tiêu đề

Slot footer cho các nút cuối hộp thoại. Sự kiện onClose khi người dùng bấm đóng hoặc ra ngoài.

TSX
<pos-modal open={open} heading="Xác nhận" onClose={() => setOpen(false)}>
  <pos-text>Đẩy đơn sang ERP?</pos-text>
  <pos-button slot="footer" onClick={() => setOpen(false)}>Huỷ</pos-button>
  <pos-button slot="footer" variant="primary" onClick={push}>Đẩy</pos-button>
</pos-modal>

Action presentation: "modal" đã có sẵn modal của POS; không cần bọc thêm pos-modal.

Dự kiến bổ sung

pos-page, pos-tabs, pos-filter-bar, pos-chart (POS vẽ biểu đồ, app chỉ truyền dữ liệu), phục vụ trang Worker UI full màn hình.