FreeGridv1.15.1English

셀 안의 버튼·링크·이미지

내장 버튼·링크·이미지 셀: 눌러도 행 선택이나 편집이 시작되지 않고, 링크는 {field} 틀을 쓰며 javascript: 주소를 막고, 렌더러는 destroy로 정리할 수 있습니다.

실행 예제버튼·링크·이미지

열기·삭제 버튼, 프로필 링크, 사진.

이 데모의 코드
import { createGrid, type ColumnDef, type Row } from '@freegridjs/core';
import { makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';

export default function mount(ctx: DemoContext) {
  const h = (en: string, ko: string) => tr(ctx.lang, { en, ko });
  const photo = (i: number) => `data:image/svg+xml;utf8,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" fill="hsl(${(i * 47) % 360} 70% 55%)"/><text x="16" y="21" font-size="14" text-anchor="middle" fill="white" font-family="sans-serif">${String.fromCharCode(65 + (i % 26))}</text></svg>`)}`;
  const data = makeRows(200).map((r, i) => ({ ...r, photo: photo(i), archived: i % 5 === 0, locked: i % 11 === 0 }));
  const grid = createGrid(ctx.host, {
    columns: [
      { field: 'photo', header: '', width: 52, image: { width: 28, height: 28, round: true, alt: p => p.row.name } },
      { field: 'name', header: h('Name', '이름'), width: 170, link: { href: '#/people/{id}', onClick: p => ctx.log(`open profile #${p.row.id}`) } },
      { field: 'email', header: h('Email', '이메일'), width: 240, link: { href: p => `mailto:${p.value}` } },
      { id: 'open', header: '', width: 90, button: { label: h('Open', '열기'), onClick: p => ctx.log(`open order #${p.row.id}`) } },
      { id: 'archive', header: '', width: 100, button: {
        label: p => (p.row.archived ? h('Restore', '복원') : h('Archive', '보관')),
        disabled: p => p.row.locked,
        onClick: p => { grid.applyTransaction({ update: [{ ...p.row, archived: !p.row.archived }] }); ctx.log(`#${p.row.id} archived = ${!p.row.archived}`); },
      } },
    ],
    data, locale: ctx.lang, getRowId: r => String(r.id), rowSelection: 'multiple',
  });
  text(ctx, h('Buttons and links act on their own: the row is not selected and no editor opens. Locked rows have a greyed-out button.',
    '버튼과 링크는 따로 동작해 행이 선택되거나 편집기가 열리지 않습니다. 잠긴 행의 버튼은 흐리게 보입니다.'));
  return grid;
}

세 컬럼 속성으로 렌더러를 쓰지 않고도 셀마다 누를 수 있는 요소를 넣습니다. 그 클릭은 요소의 것이라 포커스 이동, 행 선택·체크, 범위 선택이 일어나지 않고, 더블클릭해도 편집기가 열리지 않습니다.

버튼

const columns = [
  { id: 'open', header: '', width: 80, pinned: 'right',
    button: { label: 'Open', onClick: p => openOrder(p.row.id) } },
  { id: 'remove', header: '', width: 80,
    button: {
      label: p => (p.row.archived ? 'Restore' : 'Archive'),     // text per row
      disabled: p => p.row.locked,                               // greyed out, cannot be clicked
      onClick: p => grid.applyTransaction({ update: [{ ...p.row, archived: !p.row.archived }] }),
    } },
];
이름타입설명
labelstring | (params) => string버튼 글자(기본: 셀 값).
onClick(params) => void{ value, formatted, row, rowIndex, column }를 받습니다.
disabled(params) => boolean일부 행에서 버튼을 흐리게(누를 수 없게).
const columns = [
  // a template: {field} is replaced by the row's value (URL-encoded)
  { field: 'name', link: { href: '/customers/{id}?tab=orders' } },
  // a function, a new tab, a fixed label
  { field: 'site', link: { href: p => p.value, target: '_blank', label: () => 'Visit' } },
  { field: 'email', link: { href: p => 'mailto:' + p.value } },
  // handled in your app (a router) instead of a page load
  { field: 'orderNo', link: { href: '/orders/{orderNo}', onClick: (p, e) => router.push('/orders/' + p.row.orderNo) } },
];
이름타입설명
hrefstring | (params) => string{field} 자리표시가 있는 틀이나 함수. http(s), mailto:, tel:, 상대 주소(/, ./, #, ?)만 링크가 되고, 그 밖(javascript:)은 링크 없이 글자만 보입니다.
targetstring열 곳. '_blank'면 rel="noopener noreferrer"도 붙습니다.
label(params) => string링크 글자(기본: 서식이 적용된 값).
onClick(params, event) => void클릭을 직접 처리. 브라우저는 링크로 이동하지 않습니다.

이미지

{ field: 'photoUrl', header: 'Photo', width: 60, image: { width: 32, height: 32, round: true, alt: p => p.row.name } }
이름타입설명
width / heightnumber크기(px). 높이 기본값은 행 높이 − 8.
roundboolean원형(프로필 사진).
alt(params) => string화면 낭독기용 대체 글.

이미지는 행이 보일 때 지연 로드됩니다. http(s), data:image/, blob:, 상대 주소만 표시합니다. 엑셀 내보내기에서 images: true로 그림을 넣을 수 있습니다(엑셀: 그림·메모·보호 참고).

직접 만든 요소 정리하기

그 밖의 것은 renderer가 HTML이나 요소를 돌려줍니다. 정리가 필요한 것(React 루트, 차트, 타이머)을 붙인다면 { el, destroy }를 돌려주세요. 셀을 다시 그리기 전과 그리드를 없앨 때 destroy가 불립니다.

import { createRoot } from 'react-dom/client';

{ field: 'trend', renderer: p => {
  const el = document.createElement('div');
  const root = createRoot(el);
  root.render(<TrendChart values={p.value} />);
  return { el, destroy: () => root.unmount() };
} }

이 페이지에 대한 질문

그리드 행마다 버튼을 넣으려면?

button: { label: '열기', onClick: p => open(p.row) }가 있는 컬럼을 추가하세요. 누르면 onClick이 셀 정보를 받고, 행 선택이나 편집기는 열리지 않습니다.