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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@karrotframe/navigator-plugin

v0.6.1

Published

extension for @karrotframe/navigator

Downloads

6

Readme

@karrotframe/navigator-plugin

English

유연하고 확장가능한 @karrotframe/navigator 을 위한 plugin

  • 🧩 @karrotframe/navigator 라이프사이클 hook 을 이용한 이벤트 제어
  • 📭 라이프사이클 hook 에 미들웨어 지원으로 단계별 이벤트 제어
  • 🖇️ plugin 내부 상태 관리를 통한 다양한 사용자 시나리오 지원


설치

$ yarn add @karrotframe/navigator-plugin

간단한 plugin 만들기

plugin 정의

plugins/index.ts

import type {
  PluginType,
  NavigatorPluginType,
} from '@karrotframe/navigator-plugin'

const pluginName = 'SimplePlugin'

export const useSimplePlugin = (): PluginType & {
  pluginName: string
} => {
  return pluginName
}

export const simplePlugin: NavigatorPluginType = {
  name: pluginName,
  executor: useSimplePlugin,
}

생성한 plugin 적용하기

App.tsx

import { simplePlugin } from './plugins'

const App: React.FC = () => {
  return (
    <Navigator plugins={[simplePlugin]}>
      <Screen path="/" component={Main} />
    </Navigator>
  )
}

Main.tsx

import { useSimplePlugin } from './plugins'

const Main: React.FC = () => {
  const { pluginName } = useSimplePlugin()
  return (
    <div>
      <span>Main</span>
      <span>{pluginName}</span>
    </div>
  )
}

Navigator 의 라이프사이클 hook 을 이용한 plugin 만들기

plugin 정의

plugins/index.ts

import type {
  NavigatorPluginType,
  PluginType,
} from '@karrotframe/navigator-plugin'

export const loggerPlugin: NavigatorPluginType = {
  name: 'loggerPlugin',
  executor: (): PluginType => ({
    lifeCycleHooks: {
      onPoppedWithData: async ({ from, data }) => {
        console.log('from: ', from)
        console.log('data: ', data)
      },
    },
  }),
}

onPoppedWithData hook 은 pop().send(data) 를 호출할 때 불리는 hook 이에요.

인자로 pop() 을 호출하는 screen path 인 from 값과, pop().send(data) 에서 전달한 인자인 data 값을 받아요.

lifecycle hook 은 onPoppedWithData 이외에도 여러가지가 존재해요.


plugin 에서 state 를 관리하면서 해당 state 를 어플리케이션에서 사용하는 방법

plugin 정의

plugins/index.ts

import React, { createContext, useContext, useState, useMemo } from 'react'
import type {
  NavigatorPluginType,
  PluginType,
} from '@karrotframe/navigator-plugin'

// 전역 state 를 관리하기 위한 context 생성
export const ContextDataPlugin = createContext<{
  data: any
  setData: (data: any) => void
}>(null as any)

// 전역 state 를 전달하기 위한 provider 생성:
export const DataPluginProvider: React.FC = (props) => {
  const [data, setData] = useState<any>(null)
  return (
    <ContextDataPlugin.Provider value={{ data, setData }}>
      {props.children}
    </ContextDataPlugin.Provider>
  )
}

export const useDataPlugin = (): PluginType & {
  dataFromNextPage: (params: { from: string }) => any
} => {
  const context = useContext(ContextDataPlugin)

  return useMemo(() => {
    return {
      lifeCycleHooks: {
        onPoppedWithData: async ({ from, data }) => {
          // lifecycle hook 에서 plugin 의 state 를 제어해요
          context.setData({ [from]: data })
        },
      },
      // dataFromNextPage 을 컴포넌트에서 호출해서 컴포넌트가 plugin 의 state 에 접근 가능하도록 해요
      dataFromNextPage: ({ from }: { from: string }) => context?.data?.[from],
    }
  }, [context])
}

export const dataPlugin: NavigatorPluginType = {
  name: 'dataPlugin',
  provider: DataPluginProvider,
  executor: useDataPlugin,
}

plugin 내부에서 state 를 제어하고, 이 state 를 컴포넌트에서 접근할 수 있도록 context api 를 사용해요.

예시에서 생성한 provider 는, 플러그인을 Navigator 에 적용할 때 Navigator 를 감싸는 provider 로 작동해요.

이러한 방법으로 plugin 의 state 를 Navigator 의 전역에서 사용이 가능해요.

생성한 플러그인 적용하기

App.tsx

import { dataPlugin } from './plugins'

const App: React.FC = () => {
  return (
    <Navigator plugins={[dataPlugin]}>
      <Screen path="/" component={Main} />
      <Screen path="/other" component={Other} />
    </Navigator>
  )
}

Main.tsx

import { useDataPlugin } from './plugins'

const Main: React.FC = () => {
  const { dataFromNextPage } = useDataPlugin()
  const result = useMemo(
    () => dataFromNextPage({ from: '/other' }),
    [dataFromNextPage]
  )

  return (
    <div>
      <span>Main</span>
      <span>{result}</span>
    </div>
  )
}

lifecycle hook 에 미들웨어 적용하기

plugin 정의

import type {
  BeforePushType,
  NavigatorPluginType,
  PluginType,
} from '@karrotframe/navigator-plugin'

// hook 에 적용할 미들웨어를 하나로 묶어주는 역할을 해요
import { composeMiddlewares } from '@karrotframe/navigator-plugin'

const filterPathMiddleware = async (
  ctx: BeforePushType,
  next: () => Promise<BeforePushType | void>
): Promise<BeforePushType | void> => {
  if (ctx.to === 'not valid') {
    // 미들웨어에서 value 를 가공해서 다음 미들웨어에게 전달할 수 있어요
    await next({
      ...ctx,
      to: 'valid',
    })
  }
  // next() 에 인자를 전달하지 않은 경우, 다음 미들웨어는 hook 이 기본적으로 받는 인자를 전달 받아요
  await next()
}
const loggerMiddleware = async ({
  to,
}: BeforePushType): Promise<BeforePushType | void> => {
  console.log('to: ', to)
}

export const pluginWithMiddleware: NavigatorPluginType = {
  name: 'pluginWithMiddleware',
  executor: (): PluginType => ({
    lifeCycleHooks: {
      beforePush: composeMiddlewares<BeforePushType>([
        filterPathMiddleware,
        loggerMiddleware,
      ]),
    },
  }),
}

composeMiddlewares 로 middleware 를 묶을 경우, hook 에 전달할 콜백 함수의 두번째 인자인 next 함수를 호출 가능해요.

미들웨어로 hook 의 lifecycle 을 단계적으로 다룰 수 있고, 값을 가공해서 next() 의 인자로 전달하여

다음 미들웨어가 기본적으로 받는 hook 의 context 인자 대신, 선행한 미들웨어가 가공한 context 인자를 받을 수 있어요.

Lifecycle Hooks

beforePush

push() 를 호출 전에 콜백을 실행해요.

| name | type | description | example | | ------------------- | ----------------- | ----------------------------------------------------------------------- | ------------ | | to | String | push() 호출을 통해 이동하려는 path 에요. | "/product" | | screenInstances | IScreenInstance[] | 클라이언트에서 보고 있는 화면(screen) 인스턴스 정보를 저장한 배열이에요 | | | screenInstancePtr | number | 현재(current) 화면(screen)을 가리키는 pointer 에요. | 3 | | options | Options | push, replace, pop 을 콜백 함수에서 호출 할 수 있어요 | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onPushed

target path 로 route 이동(push)을 실행하기 직전에 콜백 함수를 실행해요.

| name | type | description | example | | ------------------- | ----------------- | ----------------------------------------------------------------------- | ------------ | | to | String | push() 호출을 통해 이동하려는 path 에요. | "/product" | | screenInstances | IScreenInstance[] | 클라이언트에서 보고 있는 화면(screen) 인스턴스 정보를 저장한 배열이에요 | | | screenInstancePtr | number | 현재(current) 화면(screen)을 가리키는 pointer 에요. | 3 | | options | Options | push, replace, pop 을 콜백 함수에서 호출 할 수 있어요 | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeReplace

replace() 를 호출 전에 콜백을 실행해요.

| name | type | description | example | | --------- | ------- | ----------------------------------------------------- | ------------ | | to | String | replace() 호출을 통해 이동하려는 path 에요. | "/account" | | options | Options | push, replace, pop 을 콜백 함수에서 호출 할 수 있어요 | |


onReplaced

target path 로 route 이동(replace)을 실행하기 직전에 콜백 함수를 실행해요.

| name | type | description | example | | --------- | ------- | ----------------------------------------------------- | ------------ | | to | String | replace() 호출을 통해 이동하려는 path 에요. | "/account" | | options | Options | push, replace, pop 을 콜백 함수에서 호출 할 수 있어요 | |


beforePop

pop() 를 호출 전에 콜백을 실행해요.

| name | type | description | example | | ------------------- | ----------------- | ----------------------------------------------------------------------- | -------------- | | from | String | pop() 호출하는 시점의 path 에요. | "/product/1" | | screenInstances | IScreenInstance[] | 클라이언트에서 보고 있는 화면(screen) 인스턴스 정보를 저장한 배열이에요 | | | screenInstancePtr | number | 현재(current) 화면(screen)을 가리키는 pointer 에요. | 3 | | options | Options | push, replace, pop 을 콜백 함수에서 호출 할 수 있어요 | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onPopped

주어진 depth 만큼 이전 route 로 이동을 실행하기 직전에 콜백 함수를 실행해요.

| name | type | description | example | | ------------------- | --------------- | ----------------------------------------------------------------------- | -------------- | | from | String | pop() 호출하는 시점의 path 에요. | "/product/1" | | screenInstances | IScreenInstance | 클라이언트에서 보고 있는 화면(screen) 인스턴스 정보를 저장한 배열이에요 | | | screenInstancePtr | number | 현재(current) 화면(screen)을 가리키는 pointer 에요. | 3 | | options | Options | push, replace, pop 을 콜백 함수에서 호출 할 수 있어요 | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onPoppedWithData

pop().send() 를 호출할 때 콜백 함수를 실행해요.

| name | type | description | example | | ------------------- | --------------- | ----------------------------------------------------------------------- | ---------------------- | | from | String | pop() 호출하는 시점의 path 에요. | "/product/1" | | data | object | pop().send() 의 인자로 전달하는 data 에요 | { name: 'John Doe' } | | screenInstances | IScreenInstance | 클라이언트에서 보고 있는 화면(screen) 인스턴스 정보를 저장한 배열이에요 | | | screenInstancePtr | number | 현재(current) 화면(screen)을 가리키는 pointer 에요. | 3 | | options | Options | push, replace, pop 을 콜백 함수에서 호출 할 수 있어요 | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeRegisterScreen

Navigator 컴포넌트에 Screen 컴포넌트를 child 컴포넌트로 선언하면 render 할 때 해당 screen 을 등록해요.

이 screen 등록 전에 콜백 함수를 호출해요.

| name | type | description | example | | --------- | --------- | ------------------------------------------------------------------------- | ------- | | screen | IScreen | Screen 컴포넌트에 props 로 전달하는 screen 정보를 담는 오브젝트에요 | | | screens | IScreen[] | Navigator 의 child 컴포넌트로 선언한 각 screen 정보를 배열에 담고 있어요. | |

screen

{
   id: '/main',
   path: '/main',
   component:  Main
}

onRegisterScreen

Navigator 컴포넌트에 Screen 컴포넌트를 child 컴포넌트로 선언하면 render 할 때 해당 screen 을 등록해요.

이 screen 을 등록한 후에 콜백 함수를 호출해요.

| name | type | description | example | | --------- | --------- | ------------------------------------------------------------------------- | ------- | | screen | IScreen | Screen 컴포넌트에 props 로 전달하는 screen 정보를 담는 오브젝트에요 | | | screens | IScreen[] | Navigator 의 child 컴포넌트로 선언한 각 screen 정보를 배열에 담고 있어요. | |

screen

{
   id: '/main',
   path: '/main',
   component:  Main
}

beforeInsertScreenInstance

push 등의 route 이벤트가 발생하면, navigator 에 등록한 screen 정보를 기반으로 ScreenInstance 를 생성해요.

이 ScreenInstance 를 어플리케이션에 등록해서, 각 screen 화면을 개별적인 인스턴스로 다루는 과정을 거칠 때

어플리케이션에 ScreenInstance 를 등록하기 전에 콜백 함수를 호출해요.

| name | type | description | example | | ----------------- | ----------------- | ----------------------------------------------------------------------------- | ------- | | screenInstance | IScreenInstance | 클라이언트에서 보고 있는 화면(screen) 인스턴스 정보를 가지고 있어요. | | | screenInstances | IScreenInstance[] | 지금까지 등록한 screenInstance 을 배열로 가지고 있어요. | | | ptr | number | 현재 등록하려는 screenInstance 를 가리키는 pointer 에요. | 5 | | options | Options | setScreenInstances 나 setScreenInstancePtr 을 콜백 함수에서 호출 할 수 있어요 | |

screenInstances

;[
  {
    id: 4,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onInsertScreenInstance

push 등의 route 이벤트가 발생하면, navigator 에 등록한 screen 정보를 기반으로 ScreenInstance 를 생성해요.

이 ScreenInstance 를 어플리케이션에 등록해서, 각 screen 화면을 개별적인 인스턴스로 다루는 과정을 거칠 때

어플리케이션에 ScreenInstance 를 등록 직후에 콜백 함수를 호출해요.

| name | type | description | example | | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | | screenInstance | IScreenInstance | 클라이언트에서 보고 있는 화면(screen) 인스턴스 정보를 가지고 있어요. | | | screenInstances | IScreenInstance[] | 지금까지 등록한 screenInstance 을 배열로 가지고 있어요. 방금 등록한 screenInstance 또한 배열 요소로 가지고 있어요. | | | ptr | number | 현재 등록하려는 screenInstance 를 가리키는 pointer 에요. | 5 | | options | Options | setScreenInstances 나 setScreenInstancePtr 을 콜백 함수에서 호출 할 수 있어요 | |

screenInstances

;[
  {
    id: 4,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeMapScreenInstance

특정 상황에서, 특정 screenInstance 의 프로퍼티를 수정하는 경우가 있어요.

이 때 특정 screenInstance 를 지정해서 프로퍼티를 수정하는 mapper 함수를 호출하기 전에

콜백 함수를 실행해요.

| name | type | description | example | | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | | screenInstances | IScreenInstance[] | 지금까지 등록한 screenInstance 을 배열로 가지고 있어요. 방금 등록한 screenInstance 또한 배열 요소로 가지고 있어요. | | | ptr | number | 현재 수정하려는 screenInstance 를 가리키는 pointer 에요. | 4 | | options | Options | mapperScreenInstance 를 콜백 함수에서 호출 할 수 있어요. | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onMapScreenInstance

특정 상황에서, 특정 screenInstance 의 프로퍼티를 수정하는 경우가 있어요.

이 때 특정 screenInstance 를 지정해서 프로퍼티를 수정하는 mapper 함수를 호출하여

특정 screenInstance 의 프로퍼티를 변경한 직후에

콜백 함수를 실행해요.

| name | type | description | example | | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | | screenInstances | IScreenInstance[] | 지금까지 등록한 screenInstance 을 배열로 가지고 있어요. 방금 등록한 screenInstance 또한 배열 요소로 가지고 있어요. | | | ptr | number | 수정한 screenInstance 를 가리키는 pointer 에요. | 4 | | options | Options | mapperScreenInstance 를 콜백 함수에서 호출 할 수 있어요. | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

beforeAddScreenInstancePromise

push 이벤트를 발생시킬 때, pop() 이벤트가 발생하거나 pop().send() 를 호출할 때

promise 인 push 이벤트를 resolve 하는 resolve 함수를 screenInstanceId 에 대응해

오브젝트로 등록해요.

이 오브젝트를 global state 에서 다루는 screenInstancePromiseMap 에 등록하기 전에

콜백 함수를 실행해요.

| name | type | description | example | | ----------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------- | ------- | | screenInstanceId | string | screenInstancePromise 를 등록하려는 screenInstanceId 값이에요. | | | screenInstances | IScreenInstance[] | 지금까지 등록한 screenInstance 을 배열로 가지고 있어요. | | | screenInstancePtr | number | 현재(current) 화면(screen)을 가리키는 pointer 에요 | 4 | | screenInstancePromise | IScreenInstancePromise | screenInstanceId 에 대응하는 screenInstance 의 push 이벤트를 resolve 하기 위한 resolve 함수를 저장한 오브젝트에요 | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onAddScreenInstancePromise

push 이벤트를 발생시킬 때, pop() 이벤트가 발생하거나 pop().send() 를 호출할 때

promise 인 push 이벤트를 resolve 하는 resolve 함수를 screenInstanceId 에 대응해

오브젝트로 등록해요.

이 오브젝트를 global state 에서 다루는 screenInstancePromiseMap 에 등록한 직후에

콜백 함수를 실행해요.

| name | type | description | example | | ----------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------- | ------- | | screenInstanceId | string | screenInstancePromise 를 등록하려는 screenInstanceId 값이에요. | | | screenInstances | IScreenInstance[] | 지금까지 등록한 screenInstance 을 배열로 가지고 있어요. | | | screenInstancePtr | number | 현재(current) 화면(screen)을 가리키는 pointer 에요 | 4 | | screenInstancePromise | IScreenInstancePromise | screenInstanceId 에 대응하는 screenInstance 의 push 이벤트를 resolve 하기 위한 resolve 함수를 저장한 오브젝트에요 | |

screenInstances

;[
  {
    id: 2,
    screenId: '/product',
    nestedRouteCount: 0,
    present: false,
    as: '/product',
  },
]

onMountNavbar

ScreenHelmet 컴포넌트에 props 를 선언하면, @karrotframe/navigator 상단의 앱바(App Bar)를 설정할 수 있어요.

상단의 앱바는 내부 구현에서 navbar 를 mount 하여 render 를 완료해요.

navbar 를 mount 하는 시점에

콜백 함수를 실행해요.

| name | type | description | example | | ------------------- | ------------------ | -------------------------------- | ------- | | screenHelmetProps | IScreenHelmetProps | ScreenHelmet 의 props 값들이에요 | |

screenHelmetProps

{
  appendLeft: "button",
  appendRight: {
    $$typeof: Symbol(react.element)
    key: null
    props: {children: '', onClick: ƒ}
    ref: null
    type: "div"
  },
  closeButtonLocation: "left",
  customBackButton: null,
  customCloseButton: null,
  disableScrollToTop: false,
  noBackButton: false,
  noBorder: false,
  noCloseButton: false,
  onTopClick: () => { console.log("hello world"); },
  preventSwipeBack: false,
  title: "Main",
  visible: true,
}

onUnmountNavbar

ScreenHelmet 컴포넌트에 props 를 선언하면, @karrotframe/navigator 상단의 앱바(App Bar)를 설정할 수 있어요.

상단의 앱바는 내부 구현에서 navbar 를 mount 하여 render 를 완료해요.

mount 된 navbar 를 unmount 하는 시점에

콜백 함수를 실행해요.

| name | type | description | example | | ------------------- | ------------------ | -------------------------------- | ------- | | screenHelmetProps | IScreenHelmetProps | ScreenHelmet 의 props 값들이에요 | |

screenHelmetProps

{
  appendLeft: "button",
  appendRight: {
    $$typeof: Symbol(react.element)
    key: null
    props: {children: '', onClick: ƒ}
    ref: null
    type: "div"
  },
  closeButtonLocation: "left",
  customBackButton: null,
  customCloseButton: null,
  disableScrollToTop: false,
  noBackButton: false,
  noBorder: false,
  noCloseButton: false,
  onTopClick: () => { console.log("hello world"); },
  preventSwipeBack: false,
  title: "Main",
  visible: true,
}

Interfaces

type NavigatorPluginType = {
  name: string
  provider?: React.FC
  executor: () => PluginType
}
interface IScreen {
  id: string
  path: string
  Component: React.ComponentType
}
interface IScreenInstance {
  id: string
  screenId: string
  nestedRouteCount?: number
  present: boolean
  as: string
}
interface IScreenInstancePromise {
  resolve: (data: any | null) => void
}
interface IScreenInstancePromise {
  title?: React.ReactNode
  appendLeft?: React.ReactNode
  appendRight?: React.ReactNode
  closeButtonLocation?: 'left' | 'right'
  customBackButton?: React.ReactNode
  customCloseButton?: React.ReactNode
  noBorder?: boolean
  disableScrollToTop?: boolean
  onTopClick?: () => void
  visible?: boolean
  preventSwipeBack?: boolean
  noBackButton?: boolean
  noCloseButton?: boolean
}