Nasaq

Line Item Editor

Editable invoice or quote lines with a product picker that fills name, price and tax, free lines, quantity, discount and exact integer totals.

PreviewOpen ↗

Code

const [lines, setLines] = useState<LineItemEditorLine[]>([]);<LineItemEditor  value={lines}  onValueChange={setLines}  products={[{ id: "p1", name: "Coffee beans", sku: "CB-1", price: 6500, taxBps: 1500 }]}  currency="SAR"  defaultTaxBps={1500}/>;

Billing · beta

Live examples and controls: Line Item Editor in the lab.

Install

npx shadcn@latest add https://docs.nasaqui.com/r/line-item-editor.json

The lines of an invoice, quote or order. Pick a product to fill its name, price and tax rate, or type a free line. Quantity, unit price, discount and tax are editable per line, and the totals are computed in integer minor units, so there is no float drift. It is controlled: you own the array of lines.

When to use

  • Quotes, invoices, purchase orders and any list of priced lines.

When not to use

Import

import { LineItemEditor, computeLineItems } from "@fadymondy/nasaq/web";

Quick start

const [lines, setLines] = useState<LineItemEditorLine[]>([]);

<LineItemEditor
  value={lines}
  onValueChange={setLines}
  products={[{ id: "p1", name: "Coffee beans", sku: "CB-1", price: 6500, taxBps: 1500 }]}
  currency="SAR"
  defaultTaxBps={1500}
/>;

API

LineItemEditorProps extends Omit<ComponentProps<"div">, "defaultValue" | "onChange" | "children">.

PropTypeDefaultDescription
value / defaultValueLineItemEditorLine[][]The lines.
onValueChange(lines) => voidnoneCalled on every edit.
productsLineItemEditorProduct[]nonePicker options. Prices are minor units.
currencystring"USD" ("SAR" in Arabic)ISO 4217 code. Sets the price decimals.
taxMode"exclusive" | "inclusive""exclusive"Whether prices contain tax.
taxRounding"line" | "invoice""line"Round tax per line or once per rate.
defaultTaxBpsnumber0Rate for lines without their own (1500 is 15%).
taxRatesnumber[][0, 500, 1500]Rates the tax select offers.
showTax, showDiscountbooleantrueShow the columns.
allowFreeLinesbooleantrueAllow lines that are not a product.
maxLinesnumbernoneCap on the number of lines.
orderDiscount / onOrderDiscountChangeLineOrderDiscountnoneA discount on the whole basket.
readOnly, disabledbooleanfalseState.
footer(totals) => ReactNodenoneRendered under the totals.
labelsLineItemEditorLabelsnoneOverrides for the built-in strings.

Money helpers

computeLineItems(items, { taxMode, taxRounding, defaultTaxBps, orderDiscount }) returns per-line gross, discount, taxable, tax and total, the totals and the tax groups. Rounding is half away from zero on BigInt products. allocateMinor shares an amount with the largest-remainder method so the parts always add up. LineItemMoney, LineItemDecimalField and LineItemActionsMenu are the small pieces the POS and ledger components reuse.

Examples

Tax-inclusive prices: <LineItemEditor taxMode="inclusive" />. Tax rounded once per rate: taxRounding="invoice".

Accessibility

KeyAction
TabMove through the fields of each line.
Arrow keysMove in the product and tax lists.
EnterPick the highlighted product.
  • Every field is named with its line, for example "Qty, Steel tumbler".
  • Line actions are in a menu button and in the context menu, so pointer and keyboard users get the same actions.

RTL & i18n

Built-in English and Arabic. Amounts use Intl and are isolated left to right. Fields accept Arabic-Indic digits.

Styling & tokens

Semantic tokens only. It uses container queries: a table at wide widths and stacked cards below. Target [data-slot="line-item-editor"].

Do / Don't

  • Do keep money in minor units in your data model.
  • Do recompute on the server with the same rounding options.
  • Don't multiply prices with floats yourself.

Lab

https://docs.nasaqui.com/?path=/docs/components-billing-line-item-editor--docs

On this page