셀 안의 버튼·링크·이미지
내장 버튼·링크·이미지 셀: 눌러도 행 선택이나 편집이 시작되지 않고, 링크는 {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 }] }),
} },
];| 이름 | 타입 | 설명 |
|---|---|---|
label | string | (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) } },
];| 이름 | 타입 | 설명 |
|---|---|---|
href | string | (params) => string | {field} 자리표시가 있는 틀이나 함수. http(s), mailto:, tel:, 상대 주소(/, ./, #, ?)만 링크가 되고, 그 밖(javascript:)은 링크 없이 글자만 보입니다. |
target | string | 열 곳. '_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 / height | number | 크기(px). 높이 기본값은 행 높이 − 8. |
round | boolean | 원형(프로필 사진). |
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이 셀 정보를 받고, 행 선택이나 편집기는 열리지 않습니다.