vfunc.js

컴포넌트

layer 2(vfunc-ui)는 엔진 위에 만든 클래스 없는 컴포넌트입니다. 수고를 덜어 주는 곳에만 쓰고, 나머지는 HTML 그대로 두면 됩니다. 1.0.0부터 vfunc 패키지에 함께 들어 있습니다.

불러오기

<link rel="stylesheet" href="css/vfunc.tokens.css">
<link rel="stylesheet" href="css/vfunc-ui.css">

<script src="dist/vfunc.min.js"></script>
<script src="dist/vfunc-ui.min.js"></script>
<!-- 그리드·차트가 필요할 때 -->
<script src="dist/vfunc-ui-data.min.js"></script>
<!-- 한국어 기본 문구 -->
<script src="dist/vfunc-ui.locale.ko.js"></script>

쓰는 법

const save = vf.vfunc({
  render: () => vf.html`
    ${vf.vsInput({ name: 'email', type: 'email', label: '이메일', required: true })}
    ${vf.vsButton({ label: '저장', variant: 'primary', action: 'save' })}`,
  delegates: [{ selector: '[data-action="save"]', eventType: 'click', onEvent: async () => {
    if (await confirmSave.open()) toast.show({ message: '저장했습니다', variant: 'success' });
  } }]
});
const confirmSave = vf.vfConfirm({ title: '저장할까요?' });
const toast = vf.vfToast();
save.mount('#app');

목록

Tier는 S(vs*만), P(vs* + vf*, vf*가 vs*로 그림), F(vf*만)입니다.

이름Tier설명예시
입력
vsFieldS직접 만든 컨트롤을 라벨·힌트·오류로 감싸고 id와 aria-describedby를 넘겨 줍니다.vf.vsField({ label: 'Color', control: (a) => vf.html`<input type="color" id="${a.id}" aria-describedby="${a.describedBy}">` })
vsInputS텍스트형 <input>. label·hint·error를 주면 연결된 필드가 됩니다.vf.vsInput({ name: 'email', type: 'email', label: 'Email', error: errors.email })
vsTextareaSvsInput과 같은 필드 props를 받는 <textarea>.vf.vsTextarea({ name: 'memo', label: 'Memo', rows: 4 })
vsSelectS옵션 목록으로 만드는 네이티브 <select>. 그룹, placeholder, multiple 지원.vf.vsSelect({ name: 'role', label: 'Role', options: ['admin', 'user'], value: 'user' })
vsCheckboxS라벨이 붙은 네이티브 체크박스.vf.vsCheckbox({ name: 'agree', label: 'I agree', checked: true })
vsRadioGroupSfieldset과 legend로 묶은 라디오 버튼.vf.vsRadioGroup({ name: 'plan', label: 'Plan', options: ['free', 'pro'], value: 'free' })
vsSwitchS켜기/끄기 스위치. role="switch"인 네이티브 체크박스.vf.vsSwitch({ name: 'notify', label: 'Notifications', checked: true })
vsSliderS네이티브 범위 슬라이더.vf.vsSlider({ name: 'volume', label: 'Volume', value: 30 })
vsProgressS네이티브 진행 막대. value가 없으면 진행 중 표시.vf.vsProgress({ label: 'Upload', value: 42, showValue: true })
vsNumberInput
vfNumberInput
P감소/증가 버튼이 있는 숫자 입력. vf*는 증감, min/max 보정, step 자릿수 반올림.vf.vfNumberInput({ name: 'qty', label: 'Quantity', value: 1, min: 1, max: 99, onChange: (e) => setQty(e.data.value) })
vsSearchInput
vfSearchInput
P지우기 버튼이 있는 검색 상자. vf*는 onSearch 디바운스(IME 조합 중 제외), Enter로 즉시 검색.vf.vfSearchInput({ debounce: 300, onSearch: (e) => load(e.data.value) })
vsPasswordInput
vfPasswordInput
P보기/숨기기 버튼(aria-pressed)이 있는 비밀번호 입력.vf.vfPasswordInput({ name: 'password', label: 'Password', autocomplete: 'new-password' })
vsChipsInput
vfChipsInput
P칩(vsTag)과 텍스트 입력. Enter·쉼표로 추가, Backspace로 삭제, 폼 전송용 hidden input.vf.vfChipsInput({ name: 'tags', label: 'Tags', value: ['ui'], max: 5, onChange: (e) => save(e.data.value) })
vsRating
vfRating
P네이티브 라디오 기반 별점(키보드 동작). readonly는 대체 텍스트가 있는 이미지.vf.vfRating({ name: 'score', value: 3, onChange: (e) => rate(e.data.value) })
vsSelectButton
vfSelectButton
Paria-pressed 토글 버튼으로 고르는 옵션. 하나 또는 multiple로 여럿.vf.vfSelectButton({ ariaLabel: 'Period', options: ['day', 'week', 'month'], value: 'week', onChange: (e) => setPeriod(e.data.value) })
vsMaskedInput
vfMaskedInput
P마스크로 서식하는 텍스트 입력(0 숫자, a 문자, * 둘 다). vf*는 커서를 유지하고 원래 값도 줍니다.vf.vfMaskedInput({ name: 'phone', label: 'Phone', mask: '000-0000-0000', onChange: (e) => save(e.data.raw) })
vsDatePicker
vfDatePicker
P'YYYY-MM-DD' 값의 네이티브 날짜 입력. vf*는 입력한 글을 정규화(IE11)하고 Date를 줍니다.vf.vfDatePicker({ name: 'due', label: 'Due date', min: new Date(), onChange: (e) => setDue(e.data.date) })
vsTimePicker
vfTimePicker
P'HH:mm' 값의 네이티브 시간 입력. vf*는 입력한 글을 정규화(IE11).vf.vfTimePicker({ name: 'at', label: 'Time', step: 900 })
vfDateRangePickerF두 네이티브 입력의 시작일·종료일. 종료일이 시작일보다 앞서지 않습니다.vf.vfDateRangePicker({ label: 'Period', names: ['from', 'to'], onChange: (e) => search(e.data.start, e.data.end) })
표시
vsButtonS변형·크기·로딩 상태와 data-action / data-ref 훅을 가진 버튼.vf.vsButton({ label: 'Save', variant: 'primary', action: 'save' })
vsButtonGroupSrole="group"로 묶은 나란한 버튼.vf.vsButtonGroup({ label: 'View', attached: true, buttons: [{ label: 'List', action: 'list' }, { label: 'Grid', action: 'grid' }] })
vsBadgeS작은 상태 표시나 개수.vf.vsBadge({ label: 'Active', variant: 'success', dot: true })
vsTagS라벨. 삭제 버튼(data-action, data-value)을 붙일 수 있습니다.vf.vsTag({ label: 'urgent', value: 'urgent', removable: true, removeAction: 'remove-tag' })
vsAvatarS사용자 사진, 또는 이름의 머리글자.vf.vsAvatar({ name: 'Ada Lovelace', src: user.photo })
vsAlertS정보·성공·경고·위험 메시지 상자. 닫기 버튼 선택.vf.vsAlert({ variant: 'warning', title: 'Check', message: 'Unsaved changes', dismissible: true })
vsCardS머리(제목·부제·동작)·본문·바닥을 가진 카드.vf.vsCard({ title: 'Sales', body: vf.html`<p>${text}</p>`, footer: vf.vsButton({ label: 'More', action: 'more' }) })
vsDescriptionsS라벨/값 쌍을 1~4열의 <dl>로.vf.vsDescriptions({ columns: 2, items: [{ label: 'Name', value: user.name }, { label: 'Email', value: user.email }] })
vsStatCardSvf.fmt로 서식한 핵심 수치와 증감(+12.5%).vf.vsStatCard({ label: 'Revenue', value: 1250000, format: { style: 'currency', currency: 'KRW' }, delta: 0.125, deltaLabel: 'vs last month' })
vsTimelineS시간 순서의 이벤트. 시간은 vf.fmt.date로 서식.vf.vsTimeline({ items: [{ title: 'Order placed', time: order.createdAt, variant: 'success' }] })
vsEmptyStateS목록이나 검색 결과가 없을 때의 화면.vf.vsEmptyState({ description: 'Try another keyword', action: vf.vsButton({ label: 'Reset', action: 'reset' }) })
vsSkeletonS로딩 자리 표시(텍스트 줄·사각형·원). 스크린리더에서는 숨김.vf.vsSkeleton({ lines: 3 })
vsSpinnerS숨은 상태 문구가 있는 로딩 표시.vf.vsSpinner({ size: 'sm' })
vsTooltipShover·focus 때 보이는 짧은 글. aria-describedby로 연결, CSS만 사용.vf.vsTooltip({ text: 'Copy link', trigger: (a) => vf.vsButton({ label: 'Copy', action: 'copy', describedBy: a.describedBy }) })
vsListView
vfListView
P항목 목록. selectable이면 로빙 포커스 listbox(화살표, Space, Enter).vf.vfListView({ label: 'Users', items: users, selectable: 'single', onSelect: (e) => open(e.data.value) })
vfCarouselF이전/다음·점 버튼과 선택적 자동 넘김(hover·focus·reduced-motion에서 멈춤)의 슬라이드.vf.vfCarousel({ label: 'News', items: [{ src: '/a.jpg', alt: 'Launch' }, { content: vf.html`<p>${text}</p>` }], autoplay: 5000 })
내비게이션
vsBreadcrumbS현재 페이지까지의 경로. 마지막 항목에 aria-current="page".vf.vsBreadcrumb({ items: [{ label: 'Home', href: '#/' }, { label: 'Users', href: '#/users' }, { label: user.name }] })
vsPagination
vfPagination
P이전/다음과 생략 표시가 있는 페이지 버튼. vf*는 페이지를 바꾸고 onChange 호출.vf.vfPagination({ total: 230, pageSize: 20, onChange: (e) => load(e.data.page) })
vsTabs
vfTabs
P탭과 패널(WAI-ARIA). 로빙 tabindex, vf*는 화살표(RTL 반영)·Home·End.vf.vfTabs({ label: 'Settings', tabs: [{ id: 'profile', label: 'Profile', content: profileHtml }, { id: 'security', label: 'Security', content: securityHtml }] })
vsAccordion
vfAccordion
P제목 버튼(aria-expanded)과 region 패널의 접이식 섹션. 하나 또는 여럿 열기.vf.vfAccordion({ items: faq.map((q) => ({ id: q.id, title: q.question, content: q.answer })) })
vsStepper
vfStepper
P진행 단계: 완료, 현재(aria-current="step"), 예정.vf.vfStepper({ steps: ['Cart', 'Shipping', 'Payment'], active: 1 })
vsSplitButton
vfSplitButton
P주 동작과 관련 동작 메뉴(vfDropdown 메뉴 공유).vf.vfSplitButton({ label: 'Save', variant: 'primary', items: [{ label: 'Save as draft', action: 'draft' }], onClick: save, onSelect: (e) => run(e.data.action) })
오버레이
vfModalF모달 대화상자. 포커스를 안에 가두고 되돌리며, Esc는 가장 위 레이어만, 배경 스크롤 잠금. footer 동작은 onAction.const dialog = vf.vfModal({ title: 'Edit user', content: form, footer: vf.vsButton({ label: 'Save', action: 'save', variant: 'primary' }), onAction: (e) => save() }); dialog.open();
vfDrawerF화면 끝·시작·아래에 붙는 전체 높이 패널 형태의 vfModal.vf.vfDrawer({ title: 'Filters', side: 'end', content: filtersHtml }).open()
vfConfirmF예/아니요 alertdialog. open()이 답의 Promise를 돌려주고, danger면 취소 버튼에서 시작.const c = vf.vfConfirm({ title: 'Delete 3 items?', variant: 'danger', confirmLabel: 'Delete' }); const ok = await c.open(); c.destroy(); if (ok) remove();
vfToastF알림 영역 하나(라이브 영역). show()로 추가하고 일정 시간 뒤 사라지며, hover·focus 중에는 유지.const toast = vf.vfToast(); toast.show({ message: 'Saved', variant: 'success' });
vfDropdownF동작 메뉴를 여는 버튼(WAI-ARIA 메뉴 버튼: 화살표, Home, End, 글자, Esc).vf.vfDropdown({ trigger: { label: 'Actions' }, items: [{ label: 'Rename', action: 'rename' }, { separator: true }, { label: 'Delete', action: 'delete', danger: true }], onSelect: (e) => run(e.data.action) })
vfPopoverF버튼 옆의 비모달 대화상자. Esc, 바깥 클릭, 포커스 이탈로 닫힘.vf.vfPopover({ trigger: { label: 'Help' }, title: 'Shortcuts', content: vf.html`<p>Press / to search.</p>` })
데이터
vsTableS정렬 헤더 버튼(aria-sort, data-action="sort"), 행 키, 빈·로딩 상태를 가진 표. vfPagination과 앱 state로 목록 화면을 만듭니다.vf.vsTable({ caption: 'Users', columns: [{ key: 'name', label: 'Name', sortable: true }, { key: 'age', label: 'Age', align: 'end' }], data: rows, sort: state.sort })
vsSparklineS대체 텍스트가 있는 작은 정적 SVG 추세(선·막대). 표와 통계 카드용.vf.vsSparkline({ data: [3, 5, 4, 8, 7], type: 'line' })
vfGridF데이터 그리드(데이터 파일): 정렬, 페이징, 필터, 행 선택, 서버 모드, 고정 헤더. G-1 그리드 계약. (vfunc-ui-data.js)vf.vfGrid({ columns, data: users, pageSize: 20, selectable: 'multiple', onSelect: (e) => setSelected(e.data.keys) })
vsChart
vfChart
PSVG 차트(데이터 파일): 막대·선·영역·원·도넛·스파크라인, 색은 --vf-chart-* 토큰. vfChart는 툴팁, 범례 토글, 폭 추적. (vfunc-ui-data.js)vf.vfChart({ type: 'line', label: 'Monthly sales', data: { labels: ['Jan', 'Feb', 'Mar'], series: [{ name: '2026', data: [12, 19, 15] }] } })

갤러리

모든 컴포넌트를 한 페이지에서 조작해 볼 수 있습니다: 컴포넌트 갤러리. 한국어·영어를 바꿔 볼 수 있고, 다크 테마는 OS 설정을 따릅니다.

AI용 목록

props와 메서드까지 담은 전체 목록은 ai/ko/components.md입니다. catalog와 타입 선언에서 빌드가 생성하므로 항상 코드와 같습니다. AI 도구에 이 파일과 llms.txt를 함께 주세요.

다른 라이브러리

AG Grid·Tabulator·Chart.js·ECharts는 공식 어댑터가 있습니다. 목록에 없는 라이브러리는 서드파티 통합의 절차대로 가져옵니다.