npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kokotu0/koko-ui

v0.7.0

Published

캘린더·중첩 카테고리 등 React UI 컴포넌트 모음

Readme

@kokotu0/koko-ui

캘린더 · 간트 · 중첩 카테고리 · Lexical 에디터 등 React UI 컴포넌트 모음.

npm i @kokotu0/koko-ui

스타일시트를 한 번 불러와야 합니다.

import '@kokotu0/koko-ui/styles.css';

엔트리

패키지는 목적별로 나뉘어 있습니다. 필요한 엔트리만 import하면 그 엔트리의 의존만 필요합니다.

| 지정자 | 내용 | JS | peer | |---|---|---|---| | @kokotu0/koko-ui | 배럴 — 캘린더 + SchedulePickr + FormBuilder | 169KB | 3 | | @kokotu0/koko-ui/calendar | 캘린더 (월/주/일/연 뷰) | 132KB | 3 | | @kokotu0/koko-ui/gantt | 간트차트 (가로 무한 스크롤, 의존성 화살표) | 57KB | 3 | | @kokotu0/koko-ui/datepickr | SchedulePickr (기간 선택) | 24KB | 1 | | @kokotu0/koko-ui/form-builder | 폼 플러그인 시스템 | 13KB | 3 | | @kokotu0/koko-ui/nested-category | 중첩 카테고리 트리 | 38KB | 3 | | @kokotu0/koko-ui/chat-input | BlockInput · DictEditor | 63KB | 11 | | @kokotu0/koko-ui/richtext | RichTextEditor — /editor로 수렴 중, 폼 블록은 /editor에서 재수출 | 23KB | 11 | | @kokotu0/koko-ui/editor | Notion형 Lexical 에디터 (폼 블록 포함) | 72KB | 17 | | @kokotu0/koko-ui/event-table | MRT 기반 이벤트 테이블 | 748KB | 10 | | @kokotu0/koko-ui/styles.css | 스타일시트 | 39KB | — |

수치는 배포 tarball의 import 그래프를 청크까지 전이 추적해 실측한 값입니다.

배럴에는 무거운 것을 넣지 않습니다. import 한 줄이 그래프 전체를 당기므로, 배럴에 있으면 optional peer 선언이 무의미해집니다. EventTable · chat-input · richtext · editor · gantt · nested-category는 서브패스 전용입니다. 기능 하나만 쓸 거면 서브패스가 항상 낫습니다.

peerDependencies — 엔트리별로 다릅니다

모든 peer는 optional로 선언돼 있습니다. 즉 npm i가 자동으로 깔아주지 않으므로, 쓰려는 엔트리에 해당하는 것을 직접 설치해야 합니다.

| 엔트리 | 함께 설치해야 하는 것 | |---|---| | 배럴 · /calendar · /gantt · /form-builder | react framer-motion lucide-react | | /datepickr | react | | /nested-category | react @mui/material @emotion/react @emotion/styled lucide-react | | /chat-input · /richtext | 위 배럴 3개 + react-dom lexical @lexical/{list,markdown,react,rich-text,selection,utils} | | /editor | react react-dom lucide-react framer-motion lexical @lexical/{clipboard,code,link,list,markdown,react,rich-text,selection,table,utils} @mui/material @mui/icons-material @emotion/react @emotion/styled | | /event-table | react react-dom lucide-react material-react-table @mui/material @mui/icons-material @emotion/react @emotion/styled @mui/x-date-pickers @tanstack/react-query @tanstack/react-virtual date-fns lodash |

이 표는 배포 tarball의 import 그래프를 청크까지 전이 추적해 뽑았고, Node ESM에서 엔트리별로 실제 import를 태워 확인했습니다. 표에 없는 것을 빼고 깔면 그 엔트리는 빠진 패키지 이름을 그대로 알려주며 실패합니다.

@emotion/react / @emotion/styled는 dist가 직접 import하지 않지만 MUI v7의 런타임 요구라 peer에 남겨 뒀습니다. @mui/x-date-pickers는 우리가 import하지 않고 material-react-table이 필수 peer로 요구해서 /event-table에 필요합니다.

optional은 "자동 설치하지 않는다"는 뜻이고 "버전 충돌을 무시한다"는 뜻이 아닙니다. 0.4.1에서 공개 엔트리가 쓰지 않는 peer 6개(zustand three @react-three/* react-router-dom @lexical/html)를 제거했습니다 — 쓰지도 않는 선언이 소비자의 npm i를 ERESOLVE로 막는 일이 있었습니다.

⚠️ QueryClientProvider가 필요한 엔트리

koko-ui/event-table 하나뿐입니다. 서버 페이지네이션·필터를 @tanstack/react-query로 돌리므로 Provider 없이 렌더하면 죽습니다.

Error: No QueryClient set, use QueryClientProvider to set one
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { EventTable } from '@kokotu0/koko-ui/event-table';

const queryClient = new QueryClient();

<QueryClientProvider client={queryClient}>
  <EventTable … />
</QueryClientProvider>

다른 엔트리는 react-query를 쓰지 않습니다.

0.5.0부터 koko-ui/editor는 react-query가 필요 없습니다. 예전에는 이미지 업로드 한 곳에서 useMutation을 쓰는 바람에 — 캐시도 queryKey도 안 쓰면서 — 에디터를 렌더하려면 소비 앱이 react-query를 채택해야 했습니다. uploader는 이미 prop으로 주입받으므로 그냥 await합니다. 라이브러리가 소비 앱의 상태 관리 스택을 결정하지 않습니다.


사용 예

캘린더

상태는 useCalendar()가 만들고, CalendarProvider의 value로 넘깁니다. 이벤트 타입은 자유롭고 날짜를 어디서 읽을지는 dateExtractor로 알려줍니다.

import { Calendar, CalendarProvider, useCalendar } from '@kokotu0/koko-ui/calendar';
import '@kokotu0/koko-ui/styles.css';

function MyCalendar({ events }) {
  const state = useCalendar({
    events,
    dateExtractor: {
      getStart: (e) => e.start,
      getEnd: (e) => e.end,
    },
  });

  return (
    <CalendarProvider value={state}>
      <Calendar />
    </CalendarProvider>
  );
}

useCalendar() 호출을 호출자에게 남겨 둔 것은 의도입니다 — 페이지가 상태를 직접 읽어야 하는 경우(쿼리 기간 동기화, 모달 초기값)가 있습니다. Provider 안에서는 슬라이스 훅으로 읽습니다: useNavigation() · useSelection() · useEvents() · useLayout() · useCalendarTheme().

간트

extractor는 개별 prop으로 평탄화되어 있습니다.

import { GanttChart } from '@kokotu0/koko-ui/gantt';

<GanttChart
  tasks={tasks}
  getTaskId={(t) => t.id}
  getTaskRange={(t) => ({ start: t.start, end: t.end })}
  getTaskLabel={(t) => t.name}
  links={links}          // 의존성 화살표 (선택)
  initialViewMode="month"
/>

부품(TaskRow / Bar / GroupSection / DependencyLayer / TimeHeader)은 공개 조립 부품입니다. Provider 없이 단독 조립해도 동작합니다 — 테마는 prop > context > lightTheme 순으로 찾고, Provider 밖에서도 던지지 않습니다.

중첩 카테고리

선택 상태는 경로 문자열의 Set입니다 (구분자 기본값 '>').

import { useState } from 'react';
import { NestedCategory } from '@kokotu0/koko-ui/nested-category';

const [selected, setSelected] = useState<Set<string>>(new Set());

<NestedCategory
  categories={categories}
  selected={selected}
  onChange={setSelected}
  expandMode="toggle"
  searchable
/>

트리 파생·조상/자손 판정이 직접 필요하면 useNestedCategory(categories, selected, onChange)가 tree · toggle · getDescendantPaths 등을 돌려줍니다.

에디터

import { Editor } from '@kokotu0/koko-ui/editor';

<Editor value={content} onChange={handleChange} uploader={myUploader} />

uploader를 주입하지 않으면 파일 업로드가 에러로 거부됩니다 — 백엔드 결합물은 소비 앱이 주입하는 구조입니다. 도메인 노드나 슬래시 메뉴 항목도 extraNodes · extraSlashItems로 넣습니다.

value는 봉투({v, doc, files})든 구 형식({text} · Yoopta)이든 그대로 넘기면 됩니다 — 에디터가 Lexical 문서로 맞춥니다(toSeedableContent, 0.6.0). 오른쪽 위 전체화면 버튼은 기본 켜짐이며 fullscreenButton={false}로 끕니다. firstLineTitle을 켜면 첫 블록이 제목(h1)이 됩니다. 슬래시 항목은 label · description · keywords · icon을 직접 갖고, onSelect(editor, { query })로 자기 동작을 붙입니다.

/richtext에서 옮겨 오는 소비자용 호환 표면도 있습니다 — value에 JSON 문자열, onTextChange(plainText, json)(봉투 없는 문서), editorRef · placeholder · autoFocus · minHeight, 마크다운 단축 입력(markdownShortcuts, 기본 켜짐), 슬래시 메뉴 초성 검색. 폼 블록(FormBlockNode)은 기본 등록되며 formPlugins={formPresets}로 폼 정의를 넘깁니다. 색은 MUI theme palette에서 나오므로 다크 테마를 쓰면 본문도 함께 어두워집니다.


테마

캘린더와 간트는 같은 테마 타입을 공유합니다(캘린더에서는 CalendarTheme 별칭). lightTheme / darkTheme를 그대로 쓰거나 mergeTheme로 부분 덮어쓰기 할 수 있습니다 — 두 번째 인자는 DeepPartial이라 필요한 가지만 적으면 됩니다.

import { lightTheme, mergeTheme } from '@kokotu0/koko-ui/calendar';

const theme = mergeTheme(lightTheme, {
  colors: {
    today: { bg: '#7c3aed' },
  },
});

<CalendarProvider value={state} theme={theme}>…</CalendarProvider>

theme을 생략하면 lightTheme입니다.


요구 환경

  • React 18 또는 19
  • ESM 전용 ("type": "module"). CommonJS require()는 지원하지 않습니다.

라이선스

MIT