FreeGridv0.8.1한국어

Spans, wrapping & layout

Cells spanning rows and columns, wrapped text with growing rows, header buttons, right-to-left and card view.

Code for this demo

import { createGrid, type ColumnDef, type GridApi, type Row } from '@freegridjs/core';
import { separator, text, toggle, tr, type DemoContext } from '../ui';

export default function mount(ctx: DemoContext) {
  const { lang } = ctx;
  const h = (en: string, ko: string) => tr(lang, { en, ko });
  const regions = [
    { region: h('Seoul', '서울'), items: [[h('Monitor', '모니터'), h('Pending payment for the March order; follow up with the buyer before the end of the week.', '3월 주문 입금 대기 중입니다. 이번 주가 끝나기 전에 구매 담당자에게 다시 연락해야 합니다.')], [h('Keyboard', '키보드'), h('Shipped.', '출고 완료.')], [h('Desk', '책상'), h('Assembly service booked for Friday afternoon, customer asked for a call 30 minutes before arrival.', '금요일 오후 조립 서비스 예약. 도착 30분 전에 전화해 달라는 요청이 있었습니다.')]] },
    { region: h('Busan', '부산'), items: [[h('Chair', '의자'), h('Back-ordered: the supplier expects new stock in two weeks.', '재고 없음: 공급처 입고까지 2주 예상.')], [h('Lamp', '조명'), h('Delivered.', '배송 완료.')]] },
    { region: h('Tokyo', '도쿄'), items: [[h('Monitor arm', '모니터 암'), h('Customs paperwork done.', '통관 서류 처리 완료.')], [h('Cable kit', '케이블 세트'), h('Gift wrap requested.', '선물 포장 요청.')]] },
  ];
  const data: Row[] = [];
  for (const r of regions) {
    r.items.forEach(([item, memo], i) => data.push({
      region: r.region, regionRows: i === 0 ? r.items.length : 1, item, memo,
      qty: 1 + ((data.length * 7) % 9), price: 30 + ((data.length * 37) % 400),
    }));
    data.push({ kind: 'note', item: h(`${r.region}: shipping cut-off is 3 pm`, `${r.region}: 오후 3시 이후 주문은 다음 날 출고`) });
  }

  const columns: ColumnDef[] = [
    // the first row of each region spans all of its rows
    { field: 'region', header: h('Region', '지역'), width: 110, rowSpan: ({ row }) => row.regionRows ?? 1 },
    // note rows span the item, memo and qty columns
    { field: 'item', header: h('Item', '품목'), width: 150, colSpan: ({ row }) => (row.kind === 'note' ? 3 : 1),
      cellClass: ({ row }) => (row.kind === 'note' ? 'demo-note' : undefined) },
    // long memos wrap; autoRowHeight grows the row
    { field: 'memo', header: h('Memo', '메모'), width: 280, wrapText: true },
    { field: 'qty', header: h('Qty', '수량'), type: 'number', width: 90 },
    {
      field: 'price', header: h('Price', '단가'), type: 'number', format: '#,##0', width: 120, headerClass: 'demo-money',
      // a help button in the header; pressing it does not sort
      headerRenderer: ({ header }) => {
        const span = document.createElement('span');
        const b = document.createElement('button');
        b.type = 'button';
        b.textContent = '?';
        b.className = 'demo-help';
        b.addEventListener('click', () => ctx.log(h('Prices are in USD, before tax.', '단가는 달러, 세전 금액입니다.')));
        span.append(`${header} `, b);
        return span;
      },
    },
  ];

  let grid: GridApi;
  let rtl = false;
  let cards = false;
  const create = () => {
    grid?.destroy();
    grid = createGrid(ctx.host, {
      columns, data, locale: lang, rangeSelection: true,
      autoRowHeight: true,
      direction: rtl ? 'rtl' : 'ltr',
      // cardView: a width in px; below it rows turn into cards (phones). 99999 forces cards here.
      cardView: cards ? 99999 : false,
    });
  };
  create();

  text(ctx, h('Region cells span their rows, note rows span three columns, long memos wrap and the row grows. Press ? in the Price header.',
    '지역 셀은 여러 행을, 안내 행은 세 칸을 차지합니다. 긴 메모는 줄바꿈되고 행 높이가 늘어납니다. 단가 헤더의 ? 버튼도 눌러 보세요.'));
  separator(ctx);
  toggle(ctx, h('Right to left (RTL)', '오른쪽→왼쪽 (RTL)'), false, on => { rtl = on; create(); ctx.log(`direction: ${on ? 'rtl' : 'ltr'}`); });
  toggle(ctx, h('Card view (phones)', '카드 보기 (휴대폰)'), false, on => { cards = on; create(); ctx.log(`cardView: ${on}`); });
  return grid!;
}