├── .browserslistrc
├── .editorconfig
├── .eslintrc.js
├── .github
├── CODEOWNERS
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── add-label.yaml
│ ├── cd.yaml
│ ├── ci.yaml
│ ├── pr-closed.yaml
│ ├── tag-with-comment.yaml
│ └── update-changelog.yaml
├── .gitignore
├── .husky
└── pre-commit
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .storybook
├── decorators.tsx
├── main.ts
├── preview.ts
└── public
│ └── mockServiceWorker.js
├── .stylelintrc.json
├── .swcrc
├── .vscode
└── settings.json
├── CHANGELOG-archive-201911.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── MIGRATION.md
├── README.md
├── chromatic.config.json
├── codecov.yml
├── examples
├── nextjs-app
│ ├── .gitignore
│ ├── README.md
│ ├── app
│ │ ├── favicon.ico
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ └── template.tsx
│ ├── lib
│ │ ├── registry.tsx
│ │ └── theme.tsx
│ ├── next.config.mjs
│ ├── package.json
│ └── tsconfig.json
└── nextjs-pages
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package.json
│ ├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ └── index.tsx
│ ├── public
│ └── favicon.ico
│ └── tsconfig.json
├── global.d.ts
├── jest-setup.js
├── jest.config.js
├── lerna.json
├── package.json
├── packages
├── ab-experiments
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── google-optimize-context
│ │ │ ├── README.md
│ │ │ ├── context.tsx
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── triple-ab-experiment-context
│ │ │ ├── README.md
│ │ │ ├── context.tsx
│ │ │ ├── index.ts
│ │ │ └── service.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── constants
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── regex.test.ts
│ │ └── regex.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── fetcher
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── add-fetchers-to-gssp.test.ts
│ │ ├── add-fetchers-to-gssp.ts
│ │ ├── auth-guarded-methods.ts
│ │ ├── factories.test.ts
│ │ ├── factories.ts
│ │ ├── fetcher.ts
│ │ ├── index.ts
│ │ ├── make-request-params.test.ts
│ │ ├── make-request-params.ts
│ │ ├── methods.ts
│ │ ├── response-handler.ts
│ │ ├── safe-parse-json.test.ts
│ │ ├── safe-parse-json.ts
│ │ ├── server-fetch.ts
│ │ ├── session-refresh.ts
│ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── i18n
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── interpolate.ts
│ │ ├── locales
│ │ │ ├── en.ts
│ │ │ ├── index.ts
│ │ │ ├── ja.ts
│ │ │ ├── ko.ts
│ │ │ └── zh-TW.ts
│ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── intersection-observer
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── lazy-loaded-intersection-observer.tsx
│ │ ├── static-intersection-observer.tsx
│ │ └── use-intersection.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── meta-tags
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── app-router
│ │ │ ├── generate-apple-smart-banner-meta.ts
│ │ │ ├── generate-common-meta.ts
│ │ │ ├── generate-essential-content-meta.ts
│ │ │ ├── generate-facebook-app-link-meta.ts
│ │ │ ├── generate-facebook-open-graph-meta.ts
│ │ │ ├── generate-triple-default-meta.ts
│ │ │ └── index.ts
│ │ ├── constants.ts
│ │ ├── index.ts
│ │ ├── pages-router
│ │ │ ├── apple-smart-banner-meta.tsx
│ │ │ ├── common-meta.tsx
│ │ │ ├── essential-content-meta.test.tsx
│ │ │ ├── essential-content-meta.tsx
│ │ │ ├── facebook-app-link-meta.tsx
│ │ │ ├── facebook-open-graph-meta.tsx
│ │ │ ├── index.ts
│ │ │ └── theme-color-meta.tsx
│ │ ├── structured-data
│ │ │ ├── article-script.tsx
│ │ │ ├── breadcrumb-list-script.tsx
│ │ │ ├── discussion-forum-posting-script.tsx
│ │ │ ├── index.ts
│ │ │ ├── local-business-script.tsx
│ │ │ ├── product-script.tsx
│ │ │ ├── qa-page-script.tsx
│ │ │ └── review-script.tsx
│ │ ├── types
│ │ │ ├── index.ts
│ │ │ ├── schema.ts
│ │ │ └── structured-data-script-props.ts
│ │ └── utils.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── middlewares
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── chain.ts
│ │ ├── index.ts
│ │ ├── refresh-session
│ │ │ ├── index.ts
│ │ │ ├── next-13.ts
│ │ │ └── next-14.ts
│ │ ├── set-web-device-id.ts
│ │ ├── types.ts
│ │ └── utils
│ │ │ ├── apply-set-cookie.ts
│ │ │ ├── get-domain.ts
│ │ │ └── get-triple-app.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── react-hooks
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── hooks.stories.tsx
│ │ ├── index.ts
│ │ ├── mocks
│ │ │ └── lottie.sample.json
│ │ ├── use-body-scroll-lock.ts
│ │ ├── use-debounce.test.ts
│ │ ├── use-debounce.ts
│ │ ├── use-error-handler.ts
│ │ ├── use-fetch.ts
│ │ ├── use-interval.test.ts
│ │ ├── use-interval.ts
│ │ ├── use-local-storage.test.ts
│ │ ├── use-local-storage.ts
│ │ ├── use-lottie.tsx
│ │ ├── use-scroll-to-anchor.ts
│ │ ├── use-scroll-to-element.ts
│ │ ├── use-session-storage.test.ts
│ │ ├── use-session-storage.ts
│ │ └── use-visibility-change.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── router
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── common
│ │ │ ├── add-web-url-base.test.tsx
│ │ │ ├── add-web-url-base.ts
│ │ │ ├── default-router.ts
│ │ │ ├── disabled-link-notifier.test.tsx
│ │ │ ├── disabled-link-notifier.ts
│ │ │ ├── router-guarded-link.test.tsx
│ │ │ ├── router-guarded-link.tsx
│ │ │ ├── target.ts
│ │ │ ├── types.ts
│ │ │ ├── use-rel.test.ts
│ │ │ └── use-rel.ts
│ │ ├── external
│ │ │ ├── hook.ts
│ │ │ ├── href-handler.ts
│ │ │ ├── index.ts
│ │ │ ├── link.tsx
│ │ │ └── utils.ts
│ │ ├── href-to-props
│ │ │ ├── index.ts
│ │ │ ├── use-href-to-props.test.tsx
│ │ │ └── use-href-to-props.ts
│ │ ├── index.ts
│ │ ├── links
│ │ │ ├── index.ts
│ │ │ ├── use-make-inlink.ts
│ │ │ ├── use-make-outlink.ts
│ │ │ ├── use-open-inlink.test.ts
│ │ │ ├── use-open-inlink.ts
│ │ │ ├── use-open-native-link.test.tsx
│ │ │ ├── use-open-native-link.ts
│ │ │ ├── use-open-outlink.test.ts
│ │ │ └── use-open-outlink.ts
│ │ ├── local
│ │ │ ├── base-path.test.ts
│ │ │ ├── base-path.ts
│ │ │ ├── hook.ts
│ │ │ ├── href-handler.ts
│ │ │ ├── index.ts
│ │ │ └── link.tsx
│ │ └── navigate
│ │ │ ├── canonization.spec.ts
│ │ │ ├── canonization.ts
│ │ │ ├── index.ts
│ │ │ ├── use-isomorphic-navigate.ts
│ │ │ ├── use-navigate.test.tsx
│ │ │ └── use-navigate.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── scroll-to-element
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── scroll-position-calculators.test.ts
│ │ ├── scroll-position-calculators.ts
│ │ ├── scroll-to-element.ts
│ │ ├── scroll.ts
│ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── standard-action-handler
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── converse.tsx
│ │ ├── copy-to-clipboard.ts
│ │ ├── fetch-api.ts
│ │ ├── handler.ts
│ │ ├── hook.ts
│ │ ├── image-download.ts
│ │ ├── index.ts
│ │ ├── initialize.ts
│ │ ├── invoke-cta.ts
│ │ ├── new-window.test.ts
│ │ ├── new-window.ts
│ │ ├── require-triple-client.tsx
│ │ ├── scroll-to-element.ts
│ │ ├── serial.ts
│ │ ├── services
│ │ │ ├── copy.ts
│ │ │ └── share.ts
│ │ ├── share.ts
│ │ ├── show-toast.ts
│ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── tds-theme
│ ├── package.json
│ ├── src
│ │ ├── foundations
│ │ │ └── colors.ts
│ │ ├── global-style.ts
│ │ ├── index.ts
│ │ ├── styled.ts
│ │ └── theme
│ │ │ ├── default.ts
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── tds-ui
│ ├── package.json
│ ├── src
│ │ ├── commons.ts
│ │ ├── components
│ │ │ ├── accordion
│ │ │ │ ├── accordion-content.tsx
│ │ │ │ ├── accordion-context.tsx
│ │ │ │ ├── accordion-folded.tsx
│ │ │ │ ├── accordion-title.tsx
│ │ │ │ ├── accordion.stories.tsx
│ │ │ │ ├── accordion.tsx
│ │ │ │ └── index.ts
│ │ │ ├── action-sheet-select
│ │ │ │ ├── action-sheet-select-button.tsx
│ │ │ │ ├── action-sheet-select-context.tsx
│ │ │ │ ├── action-sheet-select-option.tsx
│ │ │ │ ├── action-sheet-select-options.tsx
│ │ │ │ ├── action-sheet-select.stories.tsx
│ │ │ │ ├── action-sheet-select.tsx
│ │ │ │ └── index.ts
│ │ │ ├── action-sheet
│ │ │ │ ├── action-sheet-body.tsx
│ │ │ │ ├── action-sheet-context.tsx
│ │ │ │ ├── action-sheet-item.tsx
│ │ │ │ ├── action-sheet-overlay.tsx
│ │ │ │ ├── action-sheet-title.tsx
│ │ │ │ ├── action-sheet.stories.tsx
│ │ │ │ ├── action-sheet.test.tsx
│ │ │ │ ├── action-sheet.tsx
│ │ │ │ ├── constants.tsx
│ │ │ │ └── index.ts
│ │ │ ├── alert
│ │ │ │ ├── alert.stories.tsx
│ │ │ │ ├── alert.tsx
│ │ │ │ └── index.ts
│ │ │ ├── button
│ │ │ │ ├── basic-button.tsx
│ │ │ │ ├── button-base.test.tsx
│ │ │ │ ├── button-base.tsx
│ │ │ │ ├── button-container.ts
│ │ │ │ ├── button-group.ts
│ │ │ │ ├── button-icon.tsx
│ │ │ │ ├── button.stories.tsx
│ │ │ │ ├── button.tsx
│ │ │ │ ├── icon-button.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── normal-button.tsx
│ │ │ │ └── types.ts
│ │ │ ├── carousel
│ │ │ │ ├── carousel-item.tsx
│ │ │ │ ├── carousel.stories.tsx
│ │ │ │ ├── carousel.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── mocks
│ │ │ │ │ └── carousel.sample.json
│ │ │ ├── checkbox-group
│ │ │ │ ├── checkbox-group-context.tsx
│ │ │ │ ├── checkbox-group-error.tsx
│ │ │ │ ├── checkbox-group-help.tsx
│ │ │ │ ├── checkbox-group-label.tsx
│ │ │ │ ├── checkbox-group.stories.tsx
│ │ │ │ ├── checkbox-group.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── use-checkbox-group.tsx
│ │ │ ├── checkbox
│ │ │ │ ├── checkbox-base.tsx
│ │ │ │ ├── checkbox.stories.tsx
│ │ │ │ ├── checkbox.tsx
│ │ │ │ └── index.ts
│ │ │ ├── confirm-selector
│ │ │ │ ├── confirm-selector-base.tsx
│ │ │ │ ├── confirm-selector.stories.tsx
│ │ │ │ ├── confirm-selector.tsx
│ │ │ │ └── index.ts
│ │ │ ├── confirm
│ │ │ │ ├── confirm.stories.tsx
│ │ │ │ ├── confirm.tsx
│ │ │ │ └── index.ts
│ │ │ ├── container
│ │ │ │ ├── container.stories.tsx
│ │ │ │ ├── container.test.tsx
│ │ │ │ ├── container.tsx
│ │ │ │ └── index.ts
│ │ │ ├── content-elements
│ │ │ │ ├── content-elements.tsx
│ │ │ │ └── index.ts
│ │ │ ├── drawer-button
│ │ │ │ ├── drawer-button.stories.tsx
│ │ │ │ ├── drawer-button.tsx
│ │ │ │ └── index.ts
│ │ │ ├── drawer
│ │ │ │ ├── drawer.stories.tsx
│ │ │ │ ├── drawer.tsx
│ │ │ │ └── index.ts
│ │ │ ├── fieldset
│ │ │ │ ├── fieldset-context.tsx
│ │ │ │ ├── fieldset-legend.tsx
│ │ │ │ ├── fieldset.stories.tsx
│ │ │ │ ├── fieldset.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── use-fieldset.tsx
│ │ │ ├── flex-box
│ │ │ │ ├── flex-box.stories.tsx
│ │ │ │ ├── flex-box.test.tsx
│ │ │ │ ├── flex-box.tsx
│ │ │ │ └── index.ts
│ │ │ ├── form-field
│ │ │ │ ├── form-field-context.tsx
│ │ │ │ ├── form-field-error.tsx
│ │ │ │ ├── form-field-help.tsx
│ │ │ │ ├── form-field-label.tsx
│ │ │ │ ├── form-field.stories.tsx
│ │ │ │ ├── form-field.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── use-form-field-state.ts
│ │ │ ├── gender-selector
│ │ │ │ ├── gender-selector-item.tsx
│ │ │ │ ├── gender-selector.stories.tsx
│ │ │ │ ├── gender-selector.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hr
│ │ │ │ ├── hr.ts
│ │ │ │ └── index.ts
│ │ │ ├── icon
│ │ │ │ ├── icon.ts
│ │ │ │ └── index.ts
│ │ │ ├── image
│ │ │ │ ├── README.md
│ │ │ │ ├── circular.ts
│ │ │ │ ├── context.tsx
│ │ │ │ ├── fixed-dimensions-frame.tsx
│ │ │ │ ├── fixed-ratio-frame.tsx
│ │ │ │ ├── image.stories.tsx
│ │ │ │ ├── image.tsx
│ │ │ │ ├── img.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── link-indicator.tsx
│ │ │ │ ├── optimized-img.tsx
│ │ │ │ ├── overlay.tsx
│ │ │ │ ├── placeholder.tsx
│ │ │ │ └── source-url.tsx
│ │ │ ├── index.ts
│ │ │ ├── input
│ │ │ │ ├── index.ts
│ │ │ │ ├── input.stories.tsx
│ │ │ │ └── input.tsx
│ │ │ ├── label
│ │ │ │ ├── index.ts
│ │ │ │ ├── label.stories.tsx
│ │ │ │ └── label.tsx
│ │ │ ├── list
│ │ │ │ ├── index.ts
│ │ │ │ ├── list.stories.tsx
│ │ │ │ ├── list.test.tsx
│ │ │ │ └── list.tsx
│ │ │ ├── long-clickable
│ │ │ │ ├── index.ts
│ │ │ │ ├── long-clickable.stories.tsx
│ │ │ │ └── long-clickable.tsx
│ │ │ ├── modal
│ │ │ │ ├── index.ts
│ │ │ │ ├── modal-action.tsx
│ │ │ │ ├── modal-actions.tsx
│ │ │ │ ├── modal-body.tsx
│ │ │ │ ├── modal-context.tsx
│ │ │ │ ├── modal-description.tsx
│ │ │ │ ├── modal-title.tsx
│ │ │ │ ├── modal.stories.tsx
│ │ │ │ ├── modal.test.tsx
│ │ │ │ └── modal.tsx
│ │ │ ├── navbar
│ │ │ │ ├── index.ts
│ │ │ │ ├── navbar.stories.tsx
│ │ │ │ ├── navbar.tsx
│ │ │ │ ├── search-navbar.stories.tsx
│ │ │ │ └── search-navbar.tsx
│ │ │ ├── numeric-spinner
│ │ │ │ ├── index.ts
│ │ │ │ ├── numeric-spinner-base.tsx
│ │ │ │ ├── numeric-spinner.stories.tsx
│ │ │ │ └── numeric-spinner.tsx
│ │ │ ├── popup
│ │ │ │ ├── index.ts
│ │ │ │ ├── popup.stories.tsx
│ │ │ │ ├── popup.test.tsx
│ │ │ │ └── popup.tsx
│ │ │ ├── radio-group
│ │ │ │ ├── index.ts
│ │ │ │ ├── radio-group-context.tsx
│ │ │ │ ├── radio-group-error.tsx
│ │ │ │ ├── radio-group-help.tsx
│ │ │ │ ├── radio-group-label.tsx
│ │ │ │ ├── radio-group.stories.tsx
│ │ │ │ ├── radio-group.tsx
│ │ │ │ └── use-radio-group.tsx
│ │ │ ├── radio
│ │ │ │ ├── index.ts
│ │ │ │ ├── radio-base.tsx
│ │ │ │ ├── radio.stories.tsx
│ │ │ │ └── radio.tsx
│ │ │ ├── rating
│ │ │ │ ├── index.ts
│ │ │ │ ├── rating.stories.tsx
│ │ │ │ └── rating.tsx
│ │ │ ├── responsive
│ │ │ │ ├── index.ts
│ │ │ │ ├── responsive.test.tsx
│ │ │ │ └── responsive.tsx
│ │ │ ├── section
│ │ │ │ ├── index.ts
│ │ │ │ ├── section.stories.tsx
│ │ │ │ ├── section.test.tsx
│ │ │ │ └── section.tsx
│ │ │ ├── segment
│ │ │ │ ├── index.ts
│ │ │ │ ├── segment.stories.tsx
│ │ │ │ └── segment.tsx
│ │ │ ├── select
│ │ │ │ ├── index.ts
│ │ │ │ ├── select.stories.tsx
│ │ │ │ └── select.tsx
│ │ │ ├── skeleton
│ │ │ │ ├── index.ts
│ │ │ │ ├── skeleton.stories.tsx
│ │ │ │ └── skeleton.tsx
│ │ │ ├── slider
│ │ │ │ ├── handle.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── range-slider.stories.tsx
│ │ │ │ ├── range-slider.tsx
│ │ │ │ ├── single-slider.stories.tsx
│ │ │ │ ├── single-slider.tsx
│ │ │ │ ├── slider-base.tsx
│ │ │ │ ├── track.tsx
│ │ │ │ └── types.ts
│ │ │ ├── spinner
│ │ │ │ ├── index.ts
│ │ │ │ ├── rolling-spinner.stories.tsx
│ │ │ │ ├── rolling-spinner.tsx
│ │ │ │ ├── spinner.stories.tsx
│ │ │ │ └── spinner.tsx
│ │ │ ├── stack
│ │ │ │ ├── index.ts
│ │ │ │ ├── stack.stories.tsx
│ │ │ │ ├── stack.test.tsx
│ │ │ │ └── stack.tsx
│ │ │ ├── sticky-header
│ │ │ │ ├── index.ts
│ │ │ │ ├── sticky-header.stories.tsx
│ │ │ │ ├── sticky-header.test.tsx
│ │ │ │ └── sticky-header.tsx
│ │ │ ├── table
│ │ │ │ ├── index.ts
│ │ │ │ ├── table.stories.tsx
│ │ │ │ └── table.tsx
│ │ │ ├── tabs
│ │ │ │ ├── basic-tab-list.tsx
│ │ │ │ ├── basic-tab.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── pointing-tab-context.tsx
│ │ │ │ ├── pointing-tab-list.tsx
│ │ │ │ ├── pointing-tab.tsx
│ │ │ │ ├── rounded-tab-list.tsx
│ │ │ │ ├── rounded-tab.tsx
│ │ │ │ ├── tab-base.tsx
│ │ │ │ ├── tab-list-base.tsx
│ │ │ │ ├── tab-list.tsx
│ │ │ │ ├── tab-panel.tsx
│ │ │ │ ├── tab.tsx
│ │ │ │ ├── tabs-context.tsx
│ │ │ │ ├── tabs.stories.tsx
│ │ │ │ ├── tabs.test.tsx
│ │ │ │ ├── tabs.tsx
│ │ │ │ └── types.ts
│ │ │ ├── tag
│ │ │ │ ├── index.ts
│ │ │ │ ├── tag.stories.tsx
│ │ │ │ └── tag.tsx
│ │ │ ├── text
│ │ │ │ ├── index.ts
│ │ │ │ ├── text.stories.tsx
│ │ │ │ ├── text.test.tsx
│ │ │ │ ├── text.tsx
│ │ │ │ └── typography.tsx
│ │ │ ├── textarea
│ │ │ │ ├── index.ts
│ │ │ │ ├── textarea.stories.tsx
│ │ │ │ └── textarea.tsx
│ │ │ ├── tooltip
│ │ │ │ ├── index.ts
│ │ │ │ ├── tooltip.stories.tsx
│ │ │ │ └── tooltip.tsx
│ │ │ ├── video
│ │ │ │ ├── context.tsx
│ │ │ │ ├── controls.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── mute-unmute-button.tsx
│ │ │ │ ├── play-pause-button.tsx
│ │ │ │ ├── seeker.tsx
│ │ │ │ ├── sources.tsx
│ │ │ │ ├── use-video-control.ts
│ │ │ │ ├── use-video-ref.ts
│ │ │ │ ├── utils.tsx
│ │ │ │ ├── video-element.tsx
│ │ │ │ ├── video-frame.tsx
│ │ │ │ ├── video.stories.tsx
│ │ │ │ └── video.tsx
│ │ │ └── visually-hidden
│ │ │ │ ├── index.ts
│ │ │ │ ├── visually-hidden.test.tsx
│ │ │ │ └── visually-hidden.tsx
│ │ ├── index.ts
│ │ ├── mixins
│ │ │ ├── border-radius.ts
│ │ │ ├── box.ts
│ │ │ ├── centered.ts
│ │ │ ├── clearing.ts
│ │ │ ├── ellipsis.ts
│ │ │ ├── horizontal-scroll.ts
│ │ │ ├── index.ts
│ │ │ ├── layering.ts
│ │ │ ├── margin-padding.ts
│ │ │ ├── max-lines.ts
│ │ │ ├── positioning.ts
│ │ │ ├── safe-area.ts
│ │ │ └── text-style.ts
│ │ └── utils
│ │ │ ├── merge-refs.ts
│ │ │ ├── should-forward-prop.ts
│ │ │ └── unit.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── tds-widget
│ ├── .eslintignore
│ ├── .prettierignore
│ ├── .stylelintignore
│ ├── package.json
│ ├── src
│ │ ├── ad-banners
│ │ │ ├── api.ts
│ │ │ ├── content-details-banner.tsx
│ │ │ ├── horizontal-entity.tsx
│ │ │ ├── horizontal-list-view.tsx
│ │ │ ├── index.ts
│ │ │ ├── list-section.ts
│ │ │ ├── list-top-banners.tsx
│ │ │ ├── typing.ts
│ │ │ ├── vertical-entity.tsx
│ │ │ └── vertical-list-view.tsx
│ │ ├── app-banner
│ │ │ ├── app-banner.stories.tsx
│ │ │ ├── app-banner.tsx
│ │ │ └── index.ts
│ │ ├── app-installation-cta
│ │ │ ├── article-card-cta.tsx
│ │ │ ├── banner-cta.stories.tsx
│ │ │ ├── banner-cta.tsx
│ │ │ ├── chatbot-cta.tsx
│ │ │ ├── constants.ts
│ │ │ ├── elements.tsx
│ │ │ ├── floating-button-cta.stories.tsx
│ │ │ ├── floating-button-cta.tsx
│ │ │ ├── image-banner.stories.tsx
│ │ │ ├── image-banner.tsx
│ │ │ ├── index.ts
│ │ │ ├── interfaces.ts
│ │ │ ├── service.ts
│ │ │ ├── text-banner.stories.tsx
│ │ │ └── text-banner.tsx
│ │ ├── author
│ │ │ ├── author-intro.tsx
│ │ │ ├── author.stories.tsx
│ │ │ ├── author.tsx
│ │ │ └── index.ts
│ │ ├── booking-completion
│ │ │ ├── booking-completion.stories.tsx
│ │ │ └── index.tsx
│ │ ├── chat
│ │ │ ├── bubble-container
│ │ │ │ ├── bubble-container.stories.tsx
│ │ │ │ ├── bubble-container.tsx
│ │ │ │ ├── bubble-info.tsx
│ │ │ │ ├── elements.tsx
│ │ │ │ ├── icons.tsx
│ │ │ │ └── index.ts
│ │ │ ├── bubble
│ │ │ │ ├── altered.tsx
│ │ │ │ ├── bubble-ui.tsx
│ │ │ │ ├── bubble.stories.tsx
│ │ │ │ ├── bubble.tsx
│ │ │ │ ├── constants.ts
│ │ │ │ ├── elements.tsx
│ │ │ │ ├── full-text-message-view.tsx
│ │ │ │ ├── image.tsx
│ │ │ │ ├── images.stories.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── item
│ │ │ │ │ ├── image.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── text.tsx
│ │ │ │ ├── nol
│ │ │ │ │ ├── full-text-view
│ │ │ │ │ │ ├── elements.tsx
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── styles.ts
│ │ │ │ ├── parent
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── parent-message.tsx
│ │ │ │ │ ├── parent-ui.tsx
│ │ │ │ │ └── parent.stories.tsx
│ │ │ │ ├── product.tsx
│ │ │ │ ├── rich.tsx
│ │ │ │ ├── text.tsx
│ │ │ │ └── type.ts
│ │ │ ├── chat
│ │ │ │ ├── chat-room-messages
│ │ │ │ │ ├── chat-api-service.ts
│ │ │ │ │ ├── chat-message-context.tsx
│ │ │ │ │ ├── chat-room-messages-provider.tsx
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── messages.tsx
│ │ │ │ │ ├── use-chat-room-messages.ts
│ │ │ │ │ ├── use-scroll.tsx
│ │ │ │ │ └── use-unread-messages.ts
│ │ │ │ ├── chat-scroll-container.tsx
│ │ │ │ ├── constants.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── messages-reducer.ts
│ │ │ │ ├── room-context.tsx
│ │ │ │ └── scroll-context.tsx
│ │ │ ├── expired
│ │ │ │ ├── elements.tsx
│ │ │ │ ├── expired.stories.tsx
│ │ │ │ ├── expired.tsx
│ │ │ │ └── index.ts
│ │ │ ├── icons
│ │ │ │ ├── ExclamationMarkIcon.tsx
│ │ │ │ ├── arrow-bottom-16-icon.tsx
│ │ │ │ ├── arrow-right-icon.tsx
│ │ │ │ ├── arrow-top-icon.tsx
│ │ │ │ ├── reply-meesage-icon.tsx
│ │ │ │ ├── reply-message-icon.tsx
│ │ │ │ ├── select-photo-icon.tsx
│ │ │ │ ├── send-icon.tsx
│ │ │ │ ├── talk-icon.tsx
│ │ │ │ └── text-full-view-arrow-icon.tsx
│ │ │ ├── index.ts
│ │ │ ├── input-area
│ │ │ │ ├── index.tsx
│ │ │ │ ├── input-area-ui
│ │ │ │ │ ├── elements.ts
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── input-area.stories.tsx
│ │ │ │ ├── nol-input-area-ui
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── types.ts
│ │ │ │ │ └── use-input-resize-observer.ts
│ │ │ │ └── utils.ts
│ │ │ ├── list
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── reducer.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── messages
│ │ │ │ ├── date-divider.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── messages.stories.tsx
│ │ │ │ ├── type.ts
│ │ │ │ └── utils.ts
│ │ │ ├── navbar
│ │ │ │ ├── index.tsx
│ │ │ │ └── navbar.stories.tsx
│ │ │ ├── nol-theme-provider
│ │ │ │ ├── constants.ts
│ │ │ │ ├── converter.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── nol-theme-provider.tsx
│ │ │ ├── preview
│ │ │ │ ├── elements.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── preview.stories.tsx
│ │ │ │ ├── preview.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── reservation-info
│ │ │ │ ├── elements.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── reservation-info.stories.tsx
│ │ │ │ └── reservation-info.tsx
│ │ │ ├── scroll-buttons-area
│ │ │ │ ├── elements.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── scroll-buttons.tsx
│ │ │ ├── types
│ │ │ │ ├── base.ts
│ │ │ │ ├── image.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── message.ts
│ │ │ │ ├── room.ts
│ │ │ │ ├── ui.ts
│ │ │ │ ├── unread.ts
│ │ │ │ └── user.ts
│ │ │ └── utils
│ │ │ │ ├── a-tag-navigator.ts
│ │ │ │ ├── image.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── profile.ts
│ │ │ │ └── user.ts
│ │ ├── content-sharing
│ │ │ ├── content-sharing.stories.tsx
│ │ │ ├── content-sharing.tsx
│ │ │ └── index.ts
│ │ ├── date-picker
│ │ │ ├── constants.ts
│ │ │ ├── date-styles.stories.tsx
│ │ │ ├── day-picker.stories.tsx
│ │ │ ├── day-picker.tsx
│ │ │ ├── index.ts
│ │ │ ├── mixins.ts
│ │ │ ├── picker-frame.ts
│ │ │ ├── range-picker-v2.stories.tsx
│ │ │ ├── range-picker-v2
│ │ │ │ ├── index.tsx
│ │ │ │ ├── picker-frame.ts
│ │ │ │ └── range-picker.tsx
│ │ │ ├── range-picker.stories.tsx
│ │ │ ├── range-picker.test.tsx
│ │ │ ├── range-picker.tsx
│ │ │ ├── service.ts
│ │ │ ├── use-disabled-days.ts
│ │ │ ├── use-public-holidays.ts
│ │ │ └── utils.ts
│ │ ├── directions-finder
│ │ │ ├── ask-to-the-local.tsx
│ │ │ ├── constants.ts
│ │ │ ├── direction-buttons.tsx
│ │ │ ├── directions-finder.stories.tsx
│ │ │ └── index.ts
│ │ ├── flicking-carousel
│ │ │ ├── arrow-icon.tsx
│ │ │ ├── flicking-carousel.stories.tsx
│ │ │ ├── flicking-carousel.tsx
│ │ │ ├── index.ts
│ │ │ └── mocks
│ │ │ │ └── carousel.sample.json
│ │ ├── footer
│ │ │ ├── award-footer.stories.tsx
│ │ │ ├── award-footer.tsx
│ │ │ ├── button-area
│ │ │ │ ├── button.tsx
│ │ │ │ ├── dropdown.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── company-info.tsx
│ │ │ ├── constants.ts
│ │ │ ├── default-footer.tsx
│ │ │ ├── extra-link-group.tsx
│ │ │ ├── footer.json
│ │ │ ├── footer.stories.tsx
│ │ │ ├── index.ts
│ │ │ ├── link-group.tsx
│ │ │ ├── logo-footer.stories.tsx
│ │ │ ├── logo-footer.tsx
│ │ │ ├── type.ts
│ │ │ └── use-footer-info.tsx
│ │ ├── hub-form
│ │ │ ├── cell.tsx
│ │ │ ├── cta.tsx
│ │ │ ├── hub-form.stories.tsx
│ │ │ ├── hub-form.tsx
│ │ │ └── index.ts
│ │ ├── image-carousel
│ │ │ ├── carousel.tsx
│ │ │ ├── content.tsx
│ │ │ ├── image-carousel.stories.tsx
│ │ │ ├── image-carousel.tsx
│ │ │ ├── image-content.tsx
│ │ │ ├── index.ts
│ │ │ ├── mocks
│ │ │ │ ├── image-carousel.sample.json
│ │ │ │ └── video-carousel.sample.json
│ │ │ ├── page-label.tsx
│ │ │ ├── types.ts
│ │ │ └── video-content.tsx
│ │ ├── image-source
│ │ │ ├── image-source.tsx
│ │ │ └── index.ts
│ │ ├── image-viewer
│ │ │ ├── detail-viewer
│ │ │ │ ├── image.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── video.tsx
│ │ │ ├── image-viewer.stories.tsx
│ │ │ ├── image-viewer.tsx
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── listing-filter
│ │ │ ├── index.ts
│ │ │ ├── listing-filter-expanding-filter-entry.stories.tsx
│ │ │ ├── listing-filter-filter-entry.stories.tsx
│ │ │ ├── listing-filter-primary-filter-entry.stories.tsx
│ │ │ ├── listing-filter.stories.tsx
│ │ │ └── listing-filter.tsx
│ │ ├── location-properties
│ │ │ ├── index.tsx
│ │ │ ├── location-properties.stories.tsx
│ │ │ ├── location-properties.tsx
│ │ │ └── property-item.tsx
│ │ ├── map
│ │ │ ├── focus-tracker.tsx
│ │ │ ├── index.test.tsx
│ │ │ ├── index.ts
│ │ │ ├── map-view.tsx
│ │ │ ├── map.stories.tsx
│ │ │ ├── mock.ts
│ │ │ ├── mocks
│ │ │ │ └── hotel-recommandations.json
│ │ │ ├── overlay
│ │ │ │ ├── index.ts
│ │ │ │ ├── markers
│ │ │ │ │ ├── flexible-marker.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── poi-dot-marker.tsx
│ │ │ │ │ └── primary-marker
│ │ │ │ │ │ ├── bubble-marker.tsx
│ │ │ │ │ │ ├── circle-marker
│ │ │ │ │ │ ├── circle-marker-base.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── dot-marker.tsx
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── pin-marker
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── pin-marker.tsx
│ │ │ │ ├── polygon.tsx
│ │ │ │ └── polyline.tsx
│ │ │ ├── types.ts
│ │ │ └── utilities.ts
│ │ ├── media
│ │ │ ├── index.ts
│ │ │ └── media.tsx
│ │ ├── nearby-pois
│ │ │ ├── index.ts
│ │ │ ├── nearby-pois.stories.tsx
│ │ │ ├── nearby-pois.tsx
│ │ │ ├── poi-entry.tsx
│ │ │ ├── reducer.ts
│ │ │ ├── service.ts
│ │ │ └── types.ts
│ │ ├── poi-detail
│ │ │ ├── actions
│ │ │ │ ├── actions.stories.tsx
│ │ │ │ ├── actions.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── tooltip
│ │ │ │ │ └── tooltip.tsx
│ │ │ ├── area-names.tsx
│ │ │ ├── constants.ts
│ │ │ ├── copy-action-sheet-item.tsx
│ │ │ ├── copy-action-sheet.tsx
│ │ │ ├── detail-header-v2.stories.tsx
│ │ │ ├── detail-header-v2
│ │ │ │ ├── index.test.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── detail-header.stories.tsx
│ │ │ ├── detail-header
│ │ │ │ ├── business-hours-icons.tsx
│ │ │ │ ├── business-hours-note.tsx
│ │ │ │ ├── index.test.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── image-carousel.stories.tsx
│ │ │ ├── image-carousel
│ │ │ │ ├── carousel-section.tsx
│ │ │ │ ├── carousel.tsx
│ │ │ │ ├── cta-overlay.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── note.tsx
│ │ │ │ └── placeholder.tsx
│ │ │ ├── images-provider.tsx
│ │ │ ├── images-reducer.ts
│ │ │ ├── index.ts
│ │ │ ├── mocks
│ │ │ │ ├── inventory-item.json
│ │ │ │ └── recommended-articles.json
│ │ │ ├── recommended-articles.stories.tsx
│ │ │ ├── recommended-articles
│ │ │ │ ├── api-client.ts
│ │ │ │ ├── article-entry.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── more-button.tsx
│ │ │ │ ├── recommended-articles.tsx
│ │ │ │ └── types.ts
│ │ │ ├── types.ts
│ │ │ └── use-fetch-images.tsx
│ │ ├── poi-list-elements
│ │ │ ├── carousel-element.tsx
│ │ │ ├── compact-poi-list-element.tsx
│ │ │ ├── constants.ts
│ │ │ ├── extended-poi-list-element.tsx
│ │ │ ├── get-type-names.ts
│ │ │ ├── index.ts
│ │ │ ├── mocks
│ │ │ │ ├── hotels.sample.json
│ │ │ │ └── pois.sample.json
│ │ │ ├── poi-card-element.stories.tsx
│ │ │ ├── poi-card-element
│ │ │ │ ├── direction-button.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── poi-card-element.tsx
│ │ │ ├── poi-carousel-element.stories.tsx
│ │ │ ├── poi-list-element.stories.tsx
│ │ │ ├── poi-list-element.tsx
│ │ │ └── types.ts
│ │ ├── pricing
│ │ │ ├── fixed-pricing-v2.stories.tsx
│ │ │ ├── fixed-pricing-v2
│ │ │ │ ├── index.tsx
│ │ │ │ ├── purchase-button-loading-indicator.tsx
│ │ │ │ └── purchase-button.tsx
│ │ │ ├── fixed-pricing.tsx
│ │ │ ├── index.ts
│ │ │ ├── pricing.stories.tsx
│ │ │ └── pricing.tsx
│ │ ├── public-header
│ │ │ ├── categories.ts
│ │ │ ├── constants.ts
│ │ │ ├── extra-action-item.tsx
│ │ │ ├── extra-action-separator.tsx
│ │ │ ├── extra-actions-container.tsx
│ │ │ ├── header-menu-button.tsx
│ │ │ ├── index.ts
│ │ │ ├── public-header-deeplink.tsx
│ │ │ ├── public-header.spec.tsx
│ │ │ ├── public-header.stories.tsx
│ │ │ ├── public-header.tsx
│ │ │ ├── side-menu
│ │ │ │ ├── auth-button.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── menu-list.tsx
│ │ │ │ ├── overlay.tsx
│ │ │ │ ├── profile.tsx
│ │ │ │ └── type.ts
│ │ │ ├── types.ts
│ │ │ ├── use-auto-hide.ts
│ │ │ └── use-deeplink-href.ts
│ │ ├── recommended-contents
│ │ │ ├── index.ts
│ │ │ ├── mocks
│ │ │ │ └── recommended-contents.sample.json
│ │ │ ├── recommended-contents.stories.tsx
│ │ │ └── recommended-contents.tsx
│ │ ├── replies
│ │ │ ├── auto-resizing-textarea.tsx
│ │ │ ├── context.tsx
│ │ │ ├── guide-text.tsx
│ │ │ ├── hook.tsx
│ │ │ ├── index.ts
│ │ │ ├── list
│ │ │ │ ├── index.tsx
│ │ │ │ ├── not-exist-replies.tsx
│ │ │ │ └── reply.tsx
│ │ │ ├── register.test.tsx
│ │ │ ├── register.tsx
│ │ │ ├── replies-api-client.test.ts
│ │ │ ├── replies-api-client.ts
│ │ │ ├── replies.stories.tsx
│ │ │ ├── replies.tsx
│ │ │ ├── reply-tree-manipulators.test.tsx
│ │ │ ├── reply-tree-manipulators.ts
│ │ │ ├── reply.test.tsx
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── resource-list-elements
│ │ │ ├── extended-resource-list-element.stories.tsx
│ │ │ ├── extended-resource-list-element.tsx
│ │ │ ├── index.ts
│ │ │ ├── resource-list-element-stats.stories.tsx
│ │ │ ├── resource-list-element-stats.tsx
│ │ │ ├── review-scrap-stat.stories.tsx
│ │ │ └── review-scrap-stat.tsx
│ │ ├── review
│ │ │ ├── components
│ │ │ │ ├── filter-context.tsx
│ │ │ │ ├── filter.tsx
│ │ │ │ ├── full-list-button.tsx
│ │ │ │ ├── infinite-list
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── infinite-list.tsx
│ │ │ │ │ ├── latest-reviews-infinite.tsx
│ │ │ │ │ ├── popular-reviews-infinite.tsx
│ │ │ │ │ ├── rating-infinite-list.tsx
│ │ │ │ │ ├── services.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── mileage-button.tsx
│ │ │ │ ├── my-review-action-sheet.tsx
│ │ │ │ ├── others-review-action-sheet.tsx
│ │ │ │ ├── review-element
│ │ │ │ │ ├── badges.tsx
│ │ │ │ │ ├── comment.tsx
│ │ │ │ │ ├── foldable-comment.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── media
│ │ │ │ │ │ ├── compare-media.ts
│ │ │ │ │ │ ├── elements.ts
│ │ │ │ │ │ ├── image.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── media-wrapper.tsx
│ │ │ │ │ │ ├── medium.tsx
│ │ │ │ │ │ └── video.tsx
│ │ │ │ │ ├── pinned-message.tsx
│ │ │ │ │ ├── purchaseInfo.tsx
│ │ │ │ │ └── user.tsx
│ │ │ │ ├── review-placeholder-with-rating.tsx
│ │ │ │ ├── reviews-shorten.tsx
│ │ │ │ ├── reviews.tsx
│ │ │ │ ├── shorten-list
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── latest-reviews.tsx
│ │ │ │ │ ├── popular-reviews.tsx
│ │ │ │ │ ├── rating-reviews.tsx
│ │ │ │ │ ├── reviews-list.tsx
│ │ │ │ │ ├── services.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── sorting-context.tsx
│ │ │ │ ├── sorting-options-action-sheet.tsx
│ │ │ │ ├── sorting-options.tsx
│ │ │ │ ├── types.tsx
│ │ │ │ └── write-button.tsx
│ │ │ ├── constants.ts
│ │ │ ├── data
│ │ │ │ └── graphql
│ │ │ │ │ ├── client.ts
│ │ │ │ │ ├── generated.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── mutation.graphql
│ │ │ │ │ └── query.graphql
│ │ │ ├── index.ts
│ │ │ ├── mocks
│ │ │ │ ├── review-element.duo-images.json
│ │ │ │ ├── review-element.duo-videos.json
│ │ │ │ ├── review-element.mono-image.json
│ │ │ │ ├── review-element.mono-video.json
│ │ │ │ ├── review-element.more-images.json
│ │ │ │ ├── review-element.more-vidoes.json
│ │ │ │ ├── review-element.penta-images.json
│ │ │ │ ├── review-element.quad-images.json
│ │ │ │ ├── review-element.tri-images.json
│ │ │ │ ├── review-element.tri-videos.json
│ │ │ │ └── reviews.ts
│ │ │ ├── review-element.stories.tsx
│ │ │ ├── reviews-placeholder.stories.tsx
│ │ │ ├── reviews-shorten.stories.tsx
│ │ │ ├── reviews.stories.tsx
│ │ │ ├── services
│ │ │ │ ├── index.ts
│ │ │ │ ├── use-client-actions.tsx
│ │ │ │ └── use-reviews.ts
│ │ │ └── utils.ts
│ │ ├── scrap-button
│ │ │ ├── hooks.ts
│ │ │ ├── index.ts
│ │ │ ├── outline-scrap-button.stories.tsx
│ │ │ ├── outline-scrap-button.tsx
│ │ │ ├── overlay-scrap-button.stories.tsx
│ │ │ ├── overlay-scrap-button.tsx
│ │ │ ├── scrap-button-mask.test.tsx
│ │ │ ├── scrap-button-mask.tsx
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── scrap
│ │ │ ├── constants.ts
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ ├── provider.tsx
│ │ │ ├── reducer.ts
│ │ │ ├── services.ts
│ │ │ ├── types.ts
│ │ │ └── use-scrap.ts
│ │ ├── search
│ │ │ ├── index.ts
│ │ │ ├── search.stories.tsx
│ │ │ └── search.tsx
│ │ ├── social-reviews
│ │ │ ├── external-links.tsx
│ │ │ ├── index.ts
│ │ │ ├── social-review.tsx
│ │ │ └── social-reviews.stories.tsx
│ │ ├── static-map
│ │ │ ├── index.ts
│ │ │ ├── static-map.stories.tsx
│ │ │ └── static-map.tsx
│ │ └── user-verification
│ │ │ ├── confirmation-services.test.ts
│ │ │ ├── confirmation-services.ts
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── use-user-verification.test.ts
│ │ │ ├── use-user-verification.ts
│ │ │ ├── verification-request.stories.tsx
│ │ │ ├── verification-request.tsx
│ │ │ ├── verified-message.spec.tsx
│ │ │ └── verified-message.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-document
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── animation.stories.tsx
│ │ ├── elements
│ │ │ ├── anchor.tsx
│ │ │ ├── animation.tsx
│ │ │ ├── coupon
│ │ │ │ ├── coupon-download-buttons.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── modals.tsx
│ │ │ │ ├── utils.test.ts
│ │ │ │ └── utils.ts
│ │ │ ├── embedded.tsx
│ │ │ ├── external-video.tsx
│ │ │ ├── images.tsx
│ │ │ ├── index.ts
│ │ │ ├── itinerary.tsx
│ │ │ ├── itinerary
│ │ │ │ ├── badge.ts
│ │ │ │ ├── icons.tsx
│ │ │ │ ├── itinerary-map.tsx
│ │ │ │ ├── poi-card.tsx
│ │ │ │ ├── save-to-itinerary.tsx
│ │ │ │ ├── tag-label.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── use-computed-itineraries.ts
│ │ │ │ ├── use-computed-map.ts
│ │ │ │ ├── use-handle-add-pois-to-trip.ts
│ │ │ │ ├── use-safety-poi.ts
│ │ │ │ └── with-type-circle-badge.tsx
│ │ │ ├── links.tsx
│ │ │ ├── list.tsx
│ │ │ ├── note.tsx
│ │ │ ├── pois.tsx
│ │ │ ├── regions.tsx
│ │ │ ├── shared
│ │ │ │ ├── display-containers.tsx
│ │ │ │ ├── document-carousel.tsx
│ │ │ │ ├── generate-click-handler.ts
│ │ │ │ └── resource-list.tsx
│ │ │ ├── sticky-tabs.tsx
│ │ │ ├── table.tsx
│ │ │ ├── text
│ │ │ │ ├── headings.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── plain.tsx
│ │ │ └── tna
│ │ │ │ ├── index.tsx
│ │ │ │ ├── price-policy-coupon-info.tsx
│ │ │ │ ├── product.tsx
│ │ │ │ ├── slot.tsx
│ │ │ │ ├── types.ts
│ │ │ │ └── use-generate-coupon.ts
│ │ ├── heading.stories.tsx
│ │ ├── hr.stories.tsx
│ │ ├── images.stories.tsx
│ │ ├── index.ts
│ │ ├── links.stories.tsx
│ │ ├── list.stories.tsx
│ │ ├── mocks
│ │ │ ├── hotel.sample.json
│ │ │ ├── images-frame.sample.json
│ │ │ ├── images.sample.json
│ │ │ ├── pois.sample.json
│ │ │ ├── slots.sample.json
│ │ │ ├── triple-document.embedded.json
│ │ │ ├── triple-document.itinerary.json
│ │ │ ├── triple-document.regions.json
│ │ │ └── triple-document.sample.json
│ │ ├── pois.stories.tsx
│ │ ├── prop-context
│ │ │ ├── deep-link.ts
│ │ │ ├── guest-mode.ts
│ │ │ ├── image-click-handler.ts
│ │ │ ├── image-source.ts
│ │ │ ├── link-click-handler.ts
│ │ │ ├── media-config.tsx
│ │ │ └── resource-click-handler.ts
│ │ ├── tna-slot.stories.tsx
│ │ ├── triple-document.stories.tsx
│ │ ├── triple-document.tsx
│ │ ├── types.ts
│ │ └── use-resource-event-tracker.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-email-document
│ ├── package.json
│ ├── src
│ │ ├── common
│ │ │ ├── box.ts
│ │ │ ├── fluid-table.tsx
│ │ │ ├── handlebars-anchor.tsx
│ │ │ ├── index.ts
│ │ │ └── reset.ts
│ │ ├── components
│ │ │ ├── index.ts
│ │ │ ├── preview.test.tsx
│ │ │ └── preview.tsx
│ │ ├── elements
│ │ │ ├── dividers.test.tsx
│ │ │ ├── dividers.tsx
│ │ │ ├── embedded.test.tsx
│ │ │ ├── embedded.tsx
│ │ │ ├── heading.test.tsx
│ │ │ ├── heading.tsx
│ │ │ ├── images.test.tsx
│ │ │ ├── images.tsx
│ │ │ ├── index.ts
│ │ │ ├── links.test.tsx
│ │ │ ├── links.tsx
│ │ │ ├── note.test.tsx
│ │ │ ├── note.tsx
│ │ │ ├── text.test.tsx
│ │ │ └── text.tsx
│ │ ├── embedded.stories.tsx
│ │ ├── full-email-template.tsx
│ │ ├── heading.stories.tsx
│ │ ├── hr.stories.tsx
│ │ ├── images.stories.tsx
│ │ ├── index.ts
│ │ ├── links.stories.tsx
│ │ ├── note.stories.tsx
│ │ ├── preview.stories.tsx
│ │ ├── text.stories.tsx
│ │ ├── triple-email-document.test.tsx
│ │ └── triple-email-document.tsx
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-fallback-action
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── constant.ts
│ │ ├── index.ts
│ │ ├── triple-fallback-action-remover.tsx
│ │ ├── triple-fallback-action.test.tsx
│ │ ├── triple-fallback-action.tsx
│ │ └── use-triple-fallback-action-remover.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-header
│ ├── package.json
│ ├── src
│ │ ├── frame
│ │ │ ├── common.ts
│ │ │ ├── effects
│ │ │ │ ├── common.ts
│ │ │ │ ├── flying.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── rotate.tsx
│ │ │ │ ├── types.ts
│ │ │ │ └── zoom.tsx
│ │ │ ├── frame.tsx
│ │ │ ├── image.tsx
│ │ │ ├── index.ts
│ │ │ └── text.tsx
│ │ ├── index.ts
│ │ ├── layer
│ │ │ ├── index.ts
│ │ │ ├── layer.tsx
│ │ │ └── transitions
│ │ │ │ ├── fade-in-out.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── marquee.tsx
│ │ │ │ ├── rolling.tsx
│ │ │ │ └── slide.tsx
│ │ ├── lottie
│ │ │ ├── index.ts
│ │ │ ├── lottie.tsx
│ │ │ └── use-lottie.ts
│ │ ├── mocks
│ │ │ ├── framer-type.sample.json
│ │ │ └── lottie-type.sample.json
│ │ ├── motion-container.ts
│ │ ├── service.ts
│ │ ├── triple-header.stories.tsx
│ │ ├── triple-header.tsx
│ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-web-nextjs-pages
│ ├── package.json
│ ├── src
│ │ ├── helpers
│ │ │ ├── client-app.ts
│ │ │ ├── session.ts
│ │ │ └── user-agent.ts
│ │ ├── index.ts
│ │ ├── initializers
│ │ │ ├── client-app.ts
│ │ │ ├── index.ts
│ │ │ ├── session.ts
│ │ │ └── user-agent.ts
│ │ ├── providers
│ │ │ ├── app-install-cta-modal-provider.mdx
│ │ │ ├── app-install-cta-modal-provider.tsx
│ │ │ ├── event-metadata-provider.mdx
│ │ │ ├── event-metadata-provider.tsx
│ │ │ ├── event-tracking-provider.mdx
│ │ │ ├── event-tracking-provider.tsx
│ │ │ ├── index.ts
│ │ │ ├── login-cta-modal-provider.mdx
│ │ │ ├── login-cta-modal-provider.tsx
│ │ │ ├── triple-web.mdx
│ │ │ └── triple-web.tsx
│ │ └── ssr-utils
│ │ │ ├── auth-guard.test.ts
│ │ │ ├── auth-guard.ts
│ │ │ ├── get-client-app.ts
│ │ │ ├── get-session-availability.ts
│ │ │ ├── get-user-agent.ts
│ │ │ ├── index.ts
│ │ │ └── put-invalid-session-id-remover.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-web-nextjs
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── initializers
│ │ │ ├── client-app.ts
│ │ │ ├── index.ts
│ │ │ ├── session.ts
│ │ │ └── user-agent.ts
│ │ └── providers
│ │ │ ├── app-install-cta-modal-provider.mdx
│ │ │ ├── app-install-cta-modal-provider.tsx
│ │ │ ├── event-metadata-provider.mdx
│ │ │ ├── event-metadata-provider.tsx
│ │ │ ├── event-tracking-provider.mdx
│ │ │ ├── event-tracking-provider.tsx
│ │ │ ├── index.ts
│ │ │ ├── login-cta-modal-provider.mdx
│ │ │ ├── login-cta-modal-provider.tsx
│ │ │ ├── triple-web.mdx
│ │ │ └── triple-web.tsx
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-web-test-utils
│ ├── package.json
│ ├── src
│ │ ├── create-test-wrapper.tsx
│ │ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-web-utils
│ ├── package.json
│ ├── src
│ │ ├── check-client-app.test.ts
│ │ ├── check-client-app.ts
│ │ ├── index.ts
│ │ ├── regex.ts
│ │ ├── user-agent.ts
│ │ └── user.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── triple-web
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── client-app
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── use-client-app-actions.mdx
│ │ │ ├── use-client-app-actions.test.tsx
│ │ │ ├── use-client-app-actions.ts
│ │ │ ├── use-client-app-callback.mdx
│ │ │ ├── use-client-app-callback.test.tsx
│ │ │ ├── use-client-app-callback.ts
│ │ │ ├── use-client-app.mdx
│ │ │ ├── use-client-app.test.tsx
│ │ │ ├── use-client-app.ts
│ │ │ ├── use-feature-flag.mdx
│ │ │ ├── use-feature-flag.test.tsx
│ │ │ └── use-feature-flag.ts
│ │ ├── env
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── use-env.mdx
│ │ │ ├── use-env.test.tsx
│ │ │ └── use-env.ts
│ │ ├── event-tracking
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ ├── libs
│ │ │ │ └── firebase-analytics.ts
│ │ │ ├── types.ts
│ │ │ ├── use-set-firebase-user-id.mdx
│ │ │ ├── use-set-firebase-user-id.ts
│ │ │ ├── use-track-event-with-metadata.mdx
│ │ │ ├── use-track-event-with-metadata.ts
│ │ │ ├── use-track-event.mdx
│ │ │ ├── use-track-event.ts
│ │ │ ├── use-track-screen.mdx
│ │ │ ├── use-track-screen.ts
│ │ │ ├── use-triple-web-device-id.ts
│ │ │ ├── use-utm.mdx
│ │ │ ├── use-utm.ts
│ │ │ └── utils
│ │ │ │ ├── track-event.ts
│ │ │ │ └── track-screen.ts
│ │ ├── hash-router
│ │ │ ├── context.tsx
│ │ │ ├── index.ts
│ │ │ ├── use-hash-router.mdx
│ │ │ └── use-hash-router.ts
│ │ ├── i18n
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── use-i18n.mdx
│ │ │ ├── use-i18n.test.tsx
│ │ │ ├── use-i18n.ts
│ │ │ ├── use-translation.mdx
│ │ │ ├── use-translation.test.tsx
│ │ │ └── use-translation.ts
│ │ ├── index.ts
│ │ ├── modal
│ │ │ ├── app-install-cta-modal-context.ts
│ │ │ ├── components
│ │ │ │ ├── app-install-cta-modal.tsx
│ │ │ │ └── login-cta-modal.tsx
│ │ │ ├── constants.ts
│ │ │ ├── context.tsx
│ │ │ ├── index.ts
│ │ │ ├── login-cta-modal-context.ts
│ │ │ ├── types.ts
│ │ │ ├── use-app-install-cta-modal.mdx
│ │ │ ├── use-app-install-cta-modal.ts
│ │ │ ├── use-login-cta-modal.mdx
│ │ │ └── use-login-cta-modal.ts
│ │ ├── providers
│ │ │ ├── app-install-cta-modal-provider.tsx
│ │ │ ├── event-metadata-provider.tsx
│ │ │ ├── event-tracking-provider.tsx
│ │ │ ├── index.ts
│ │ │ ├── login-cta-modal-provider.tsx
│ │ │ └── triple-web.tsx
│ │ ├── session
│ │ │ ├── context.tsx
│ │ │ ├── index.ts
│ │ │ ├── types.tsx
│ │ │ ├── use-login.mdx
│ │ │ ├── use-login.ts
│ │ │ ├── use-logout.mdx
│ │ │ ├── use-logout.ts
│ │ │ ├── use-session-availability.mdx
│ │ │ ├── use-session-availability.test.tsx
│ │ │ ├── use-session-availability.ts
│ │ │ ├── use-session-callback.mdx
│ │ │ ├── use-session-callback.test.tsx
│ │ │ ├── use-session-callback.ts
│ │ │ ├── use-session.mdx
│ │ │ ├── use-session.test.tsx
│ │ │ ├── use-session.ts
│ │ │ └── utils
│ │ │ │ ├── redirect.spec.ts
│ │ │ │ └── redirect.ts
│ │ └── user-agent
│ │ │ ├── context.tsx
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── use-user-agent.mdx
│ │ │ ├── use-user-agent.test.tsx
│ │ │ └── use-user-agent.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── type-definitions
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── geojson.ts
│ │ ├── image.ts
│ │ ├── index.ts
│ │ ├── inventory-item.ts
│ │ ├── listing-poi.ts
│ │ ├── translated-property.ts
│ │ └── triple-document.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
└── view-utilities
│ ├── README.md
│ ├── package.json
│ ├── src
│ ├── debounce.ts
│ ├── derive-current-state-and-count.ts
│ ├── find-folded-position.ts
│ ├── format-number.spec.ts
│ ├── format-number.ts
│ ├── generate-deep-link
│ │ ├── README.md
│ │ ├── index.ts
│ │ ├── make-deep-link-generator.test.ts
│ │ ├── make-deep-link-generator.ts
│ │ ├── param-injectors.test.ts
│ │ └── param-injectors.ts
│ ├── generate-share-image-url.ts
│ ├── index.ts
│ ├── measure-distance.spec.ts
│ ├── measure-distance.ts
│ ├── normalize-query-keys
│ │ ├── index.test.ts
│ │ └── index.ts
│ ├── routelist
│ │ ├── index.ts
│ │ ├── routelist.spec.ts
│ │ └── routelist.ts
│ ├── strict-query
│ │ ├── index.test.ts
│ │ └── index.ts
│ ├── timestamp.ts
│ ├── url.spec.ts
│ └── url.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.mts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── renovate.json
├── scripts
└── changelog.js
├── stories
└── introduction.mdx
├── tsconfig.json
├── tsconfig.test.json
└── vite.config.mts
/.browserslistrc:
--------------------------------------------------------------------------------
1 | [production]
2 | iOS >= 13.4, last 3 Safari versions, > 0.5% in KR, not dead
3 |
4 | [development]
5 | last 1 chrome version
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*.{js,ts,tsx}]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_style = space
8 | indent_size = 2
9 | insert_final_newline = true
10 | max_line_length = 80
11 | trim_trailing_whitespace = true
12 |
13 | [*.{json,yml,yaml}]
14 | charset = utf-8
15 | end_of_line = lf
16 | indent_style = space
17 | indent_size = 2
18 | insert_final_newline = true
19 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /** @type import('eslint').Linter.Config */
2 | module.exports = {
3 | root: true,
4 | extends: [
5 | '@titicaca/eslint-config-triple',
6 | '@titicaca/eslint-config-triple/frontend',
7 | '@titicaca/eslint-config-triple/prettier',
8 | 'plugin:storybook/recommended',
9 | ],
10 | overrides: [
11 | {
12 | files: ['*.test.*', '*.spec.*'],
13 | extends: [
14 | 'plugin:jest/style',
15 | 'plugin:jest/recommended',
16 | 'plugin:jest-dom/recommended',
17 | 'plugin:testing-library/react',
18 | ],
19 | },
20 | ],
21 | }
22 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @titicacadev/frontend
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | pnpm exec lint-staged
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | pnpm-lock.yaml
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | "@titicaca/prettier-config-triple"
2 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@titicaca/stylelint-config-triple"]
3 | }
4 |
--------------------------------------------------------------------------------
/.swcrc:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/swcrc",
3 | "module": {
4 | "type": "commonjs"
5 | },
6 | "jsc": {
7 | "parser": {
8 | "syntax": "typescript",
9 | "tsx": true
10 | },
11 | "transform": {
12 | "react": {
13 | "runtime": "automatic"
14 | }
15 | },
16 | "experimental": {
17 | "plugins": [["@swc/plugin-styled-components", {}]]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [{ "mode": "auto" }]
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Triple Frontend
2 |
3 | [](https://codecov.io/gh/titicacadev/triple-frontend)
4 |
5 | 트리플 프론트엔드 공용 컴포넌트 및 라이브러리입니다.
6 |
7 | ## 문서
8 |
9 | [Triple Frontend Storybook](https://storybook.triple-corp.com)에서 컴포넌트 문서와 예시를 볼 수 있습니다.
10 |
11 | ## 기여하기
12 |
13 | [CONTRIBUTING.md](./CONTRIBUTING.md)를 참고해주세요.
14 |
15 | ## 관련 서비스
16 |
17 | - [트리플](https://triple.guide)
18 | - [TRIPLE Korea](https://triple.global)
19 |
20 | ## 라이선스
21 |
22 | [MIT License](./LICENSE)
23 |
--------------------------------------------------------------------------------
/chromatic.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "projectId": "Project:617b7c4431c922004a42c7cc",
3 | "onlyChanged": true,
4 | "zip": true
5 | }
6 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | target: auto
6 | threshold: 10%
7 | patch: off
8 |
--------------------------------------------------------------------------------
/examples/nextjs-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/nextjs-app/README.md:
--------------------------------------------------------------------------------
1 | # Next.js App Example
2 |
3 | `triple-frontend` 모노레포 패키지를 로컬 개발 환경에서 테스트하고 통합하기 위한 실험용 프로젝트입니다. `triple-frontend` 패키지가 Next.js App Router 환경에서 어떻게 작동하는지 탐구할 수 있도록 간단한 설정을 제공합니다.
4 |
--------------------------------------------------------------------------------
/examples/nextjs-app/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titicacadev/triple-frontend/1c8f67c41cdb2f57020480af1f22e0feb319a708/examples/nextjs-app/app/favicon.ico
--------------------------------------------------------------------------------
/examples/nextjs-app/app/page.tsx:
--------------------------------------------------------------------------------
1 | export default function Home() {
2 | return null
3 | }
4 |
--------------------------------------------------------------------------------
/examples/nextjs-app/lib/theme.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { ThemeProvider as StyledThemeProvider } from 'styled-components'
4 | import { defaultTheme, GlobalStyle } from '@titicaca/tds-theme'
5 | import { PropsWithChildren } from 'react'
6 |
7 | export default function ThemeProvider({ children }: PropsWithChildren) {
8 | return (
9 |
10 |
11 | {children}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/examples/nextjs-app/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | compiler: {
4 | styledComponents: true,
5 | },
6 | }
7 |
8 | export default nextConfig
9 |
--------------------------------------------------------------------------------
/examples/nextjs-pages/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/nextjs-pages/README.md:
--------------------------------------------------------------------------------
1 | # Next.js Pages Example
2 |
3 | `triple-frontend` 모노레포 패키지를 로컬 개발 환경에서 테스트하고 통합하기 위한 실험용 프로젝트입니다. `triple-frontend` 패키지가 Next.js Pages Router 환경에서 어떻게 작동하는지 탐구할 수 있도록 간단한 설정을 제공합니다.
4 |
--------------------------------------------------------------------------------
/examples/nextjs-pages/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | compiler: {
5 | styledComponents: true,
6 | },
7 | transpilePackages: [
8 | '@titicaca/tds-theme',
9 | '@titicaca/tds-ui',
10 | '@titicaca/triple-web',
11 | '@titicaca/triple-web-nextjs-pages',
12 | ],
13 | }
14 |
15 | export default nextConfig
16 |
--------------------------------------------------------------------------------
/examples/nextjs-pages/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/examples/nextjs-pages/pages/index.tsx:
--------------------------------------------------------------------------------
1 | export default function Home() {
2 | return null
3 | }
4 |
--------------------------------------------------------------------------------
/examples/nextjs-pages/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titicacadev/triple-frontend/1c8f67c41cdb2f57020480af1f22e0feb319a708/examples/nextjs-pages/public/favicon.ico
--------------------------------------------------------------------------------
/examples/nextjs-pages/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true
15 | },
16 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
17 | "exclude": ["node_modules"]
18 | }
19 |
--------------------------------------------------------------------------------
/global.d.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom'
2 | import 'jest-styled-components'
3 |
--------------------------------------------------------------------------------
/jest-setup.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom'
2 | import 'jest-styled-components'
3 |
4 | import { TextDecoder, TextEncoder } from 'util'
5 |
6 | global.TextDecoder = TextDecoder
7 | global.TextEncoder = TextEncoder
8 |
9 | class ResizeObserver {
10 | observe() {}
11 |
12 | unobserve() {}
13 |
14 | disconnect() {}
15 | }
16 |
17 | global.ResizeObserver = ResizeObserver
18 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json",
3 | "npmClient": "pnpm",
4 | "version": "14.0.13"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/ab-experiments/README.md:
--------------------------------------------------------------------------------
1 | # ab-experiments
2 |
3 | A/B 테스트를 지원하는 패키지입니다.
4 |
5 | ## 인터페이스 종류
6 |
7 | ### 1. triple-ab-experiment-context
8 |
9 | **트리플의 사용자 ID(내부 API를 이용)**를 이용하여 A/B 테스트를 도와주는 context입니다.
10 |
11 | ### 2. google-optimize-context
12 |
13 | **구글 옵티마이즈**를 이용하여 세션을 통해 A/B 테스트를 도와주는 context입니다.
14 |
--------------------------------------------------------------------------------
/packages/ab-experiments/src/google-optimize-context/index.ts:
--------------------------------------------------------------------------------
1 | export * from './context'
2 |
--------------------------------------------------------------------------------
/packages/ab-experiments/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './google-optimize-context'
2 | export * from './triple-ab-experiment-context'
3 |
--------------------------------------------------------------------------------
/packages/ab-experiments/src/triple-ab-experiment-context/index.ts:
--------------------------------------------------------------------------------
1 | export * from './context'
2 | export * from './service'
3 |
--------------------------------------------------------------------------------
/packages/ab-experiments/src/triple-ab-experiment-context/service.ts:
--------------------------------------------------------------------------------
1 | import { authGuardedFetchers, RequestOptions } from '@titicaca/fetcher'
2 |
3 | export interface TripleABExperimentMeta {
4 | testId: number
5 | group: string
6 | }
7 |
8 | export async function getTripleABExperiment(
9 | slug: string,
10 | options?: RequestOptions,
11 | ) {
12 | return authGuardedFetchers.get(
13 | `/api/abtest/${slug}`,
14 | options,
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/packages/ab-experiments/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/ab-experiments/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/ab-experiments/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/constants/README.md:
--------------------------------------------------------------------------------
1 | # `@titicaca/constants`
2 |
3 | 트리플 프론트엔드의 공통 상수를 모아놓는 패키지입니다.
4 |
5 | - 공통 상수
6 | - 공통 정규 표현식
7 |
--------------------------------------------------------------------------------
/packages/constants/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './regex'
2 |
3 | /** API 호출 시 필요한 사용자 세션 HTTP 헤더 키 */
4 | export const SESSION_KEY = 'x-soto-session'
5 | export const TP_TK = 'TP_TK'
6 | export const TP_SE = 'TP_SE'
7 | export const X_TRIPLE_WEB_DEVICE_ID = 'x-triple-web-device-id'
8 |
--------------------------------------------------------------------------------
/packages/constants/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/constants/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/constants/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/fetcher/src/safe-parse-json.test.ts:
--------------------------------------------------------------------------------
1 | import { Response } from 'node-fetch'
2 |
3 | import safeParseJson from './safe-parse-json'
4 |
5 | it('JSON 파싱 에러를 조용히 넘깁니다.', async () => {
6 | const response = new Response('', {
7 | headers: {
8 | 'content-type': 'application/json',
9 | },
10 | })
11 |
12 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
13 | const json = await safeParseJson(response as any)
14 | expect(json).toBeUndefined()
15 | })
16 |
--------------------------------------------------------------------------------
/packages/fetcher/src/safe-parse-json.ts:
--------------------------------------------------------------------------------
1 | export default async function safeParseJson(
2 | response: Response,
3 | ): Promise {
4 | try {
5 | const json = await response.json()
6 | return json
7 | } catch {
8 | return undefined
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/fetcher/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/fetcher/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/fetcher/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/i18n/src/index.ts:
--------------------------------------------------------------------------------
1 | export { locales } from './locales'
2 | export { interpolate } from './interpolate'
3 | export * from './types'
4 |
--------------------------------------------------------------------------------
/packages/i18n/src/interpolate.ts:
--------------------------------------------------------------------------------
1 | export const interpolate = (
2 | text: string,
3 | values: Record = {},
4 | ) => {
5 | return text.replace(/{{(\w+)}}/g, (match, key) => {
6 | return values[key] !== undefined ? values[key] : match
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/packages/i18n/src/locales/index.ts:
--------------------------------------------------------------------------------
1 | import en from './en'
2 | import ja from './ja'
3 | import ko from './ko'
4 | import zhTw from './zh-TW'
5 |
6 | export const locales = { en, ja, ko, zhTw }
7 |
--------------------------------------------------------------------------------
/packages/i18n/src/types.ts:
--------------------------------------------------------------------------------
1 | import ko from './locales/ko'
2 |
3 | export type I18nKeys = typeof ko
4 |
--------------------------------------------------------------------------------
/packages/i18n/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/i18n/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/i18n/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/intersection-observer/README.md:
--------------------------------------------------------------------------------
1 | # intersection-observer
2 |
3 | intersection 관련 컴포넌트 및 Hook을 제공합니다.
4 |
5 | ## Usage
6 |
7 | ### use-intersection
8 |
9 | 기본 사용법은 아래와 같이 useIntersection의 사용하고자 하는 타입 ex) `HTMLDivElement`지정 그리고 `threshold, rootMargin` 값을 Optional 하게 사용하여 목적에 맞는 ref 를 생성 후 `isIntersecting`를 확인하고자 하는 태그에 해당 ref를 설정합니다.
10 |
11 | ```js
12 | import { useIntersection } from '@titicaca/intersection-observer';
13 |
14 | const { ref, isIntersecting } = useIntersection({ threshold, rootMargin }
15 |
16 | return (
17 |
18 | )
19 |
20 | ```
21 |
--------------------------------------------------------------------------------
/packages/intersection-observer/src/index.ts:
--------------------------------------------------------------------------------
1 | import IntersectionObserver from './lazy-loaded-intersection-observer'
2 |
3 | export { default as StaticIntersectionObserver } from './static-intersection-observer'
4 | export { default as useIntersection } from './use-intersection'
5 | export default IntersectionObserver
6 |
--------------------------------------------------------------------------------
/packages/intersection-observer/src/static-intersection-observer.tsx:
--------------------------------------------------------------------------------
1 | import 'intersection-observer'
2 | import ReactIntersectionObserver from '@titicaca/react-intersection-observer'
3 |
4 | export default ReactIntersectionObserver
5 |
--------------------------------------------------------------------------------
/packages/intersection-observer/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/intersection-observer/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/intersection-observer/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/app-router/generate-apple-smart-banner-meta.ts:
--------------------------------------------------------------------------------
1 | import { Metadata } from 'next'
2 |
3 | import { DEFAULT_APP_ID } from '../constants'
4 |
5 | export function generateAppleSmartBannerMeta({
6 | appId = DEFAULT_APP_ID,
7 | appPath = '/',
8 | }: {
9 | appId?: string
10 | appPath?: string
11 | } = {}): Metadata {
12 | const appUrlScheme = process.env.NEXT_PUBLIC_APP_URL_SCHEME || ''
13 |
14 | return {
15 | itunes: {
16 | appId,
17 | appArgument: `${appUrlScheme}://${appPath}`,
18 | },
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/app-router/generate-common-meta.ts:
--------------------------------------------------------------------------------
1 | import { Metadata } from 'next'
2 |
3 | export function generateCommonMeta(): Metadata {
4 | return {
5 | icons: {
6 | apple: 'https://triple.guide/icons/favicon-152x152.png',
7 | },
8 | manifest: '/manifest.json',
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/app-router/generate-essential-content-meta.ts:
--------------------------------------------------------------------------------
1 | import { Metadata } from 'next'
2 |
3 | export function generateEssentialContentMeta({
4 | title,
5 | description,
6 | canonicalUrl,
7 | }: {
8 | title?: string
9 | description?: string
10 | canonicalUrl?: string
11 | } = {}): Metadata {
12 | return {
13 | title: title || process.env.NEXT_PUBLIC_DEFAULT_PAGE_TITLE || '',
14 | description:
15 | description || process.env.NEXT_PUBLIC_DEFAULT_PAGE_DESCRIPTION || '',
16 | alternates: {
17 | canonical: canonicalUrl,
18 | },
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/app-router/index.ts:
--------------------------------------------------------------------------------
1 | export * from './generate-common-meta'
2 | export * from './generate-apple-smart-banner-meta'
3 | export * from './generate-essential-content-meta'
4 | export * from './generate-facebook-app-link-meta'
5 | export * from './generate-facebook-open-graph-meta'
6 | export * from './generate-triple-default-meta'
7 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_APP_ID = '1225499481'
2 | export const DEFAULT_APP_PACKAGE_NAME = 'com.titicacacorp.triple'
3 | export const DEFAULT_THEME_COLOR = '#1FC1B6'
4 | export const DEFAULT_OG_IMAGE = {
5 | url: 'https://assets.triple.guide/images/og-tag-app_download@4x.png',
6 | width: 260,
7 | height: 260,
8 | }
9 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './app-router'
3 | export * from './pages-router'
4 | export * from './structured-data'
5 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/pages-router/apple-smart-banner-meta.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import { useEnv } from '@titicaca/triple-web'
3 |
4 | import { DEFAULT_APP_ID } from '../constants'
5 |
6 | export function AppleSmartBannerMeta({
7 | appId = DEFAULT_APP_ID,
8 | appPath = '/',
9 | }: {
10 | appId?: string
11 | appPath?: string
12 | }) {
13 | const { appUrlScheme } = useEnv()
14 |
15 | return (
16 |
17 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/pages-router/index.ts:
--------------------------------------------------------------------------------
1 | export { EssentialContentMeta } from './essential-content-meta'
2 | export { CommonMeta } from './common-meta'
3 | export { AppleSmartBannerMeta } from './apple-smart-banner-meta'
4 | export { FacebookAppLinkMeta } from './facebook-app-link-meta'
5 | export { FacebookOpenGraphMeta } from './facebook-open-graph-meta'
6 | export { ThemeColorMeta } from './theme-color-meta'
7 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/pages-router/theme-color-meta.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 |
3 | import { ThemeColor } from '../types'
4 | import { DEFAULT_THEME_COLOR } from '../constants'
5 |
6 | export function ThemeColorMeta({
7 | content = DEFAULT_THEME_COLOR,
8 | }: {
9 | content?: ThemeColor | string
10 | }) {
11 | return (
12 |
13 |
14 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/structured-data/article-script.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script'
2 |
3 | import { createScript } from '../utils'
4 | import { ArticleScriptProps } from '../types'
5 |
6 | export function ArticleScript(props: ArticleScriptProps) {
7 | const articleScript = createScript(props, 'Article')
8 |
9 | return (
10 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/structured-data/breadcrumb-list-script.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script'
2 |
3 | import { createScript } from '../utils'
4 | import { BreadcrumbListScriptProps } from '../types'
5 |
6 | export function BreadcrumbListScript(props: BreadcrumbListScriptProps) {
7 | const breadcrumbScript = createScript(props, 'BreadcrumbList')
8 |
9 | return (
10 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/structured-data/index.ts:
--------------------------------------------------------------------------------
1 | export { ArticleScript } from './article-script'
2 | export { BreadcrumbListScript } from './breadcrumb-list-script'
3 | export { ProductScript } from './product-script'
4 | export { LocalBusinessScript } from './local-business-script'
5 | export { ReviewScript } from './review-script'
6 | export { QaPageScript } from './qa-page-script'
7 | export { DiscussionForumPostingScript } from './discussion-forum-posting-script'
8 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/structured-data/product-script.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script'
2 |
3 | import { createScript } from '../utils'
4 | import { ProductScriptProps } from '../types'
5 |
6 | export function ProductScript(props: ProductScriptProps) {
7 | const productScript = createScript(props, 'Product')
8 |
9 | return (
10 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/structured-data/qa-page-script.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script'
2 |
3 | import { createScript } from '../utils'
4 | import { QaPageScriptProps } from '../types'
5 |
6 | export function QaPageScript(props: QaPageScriptProps) {
7 | const qaPageScript = createScript(props, 'QAPage')
8 |
9 | return (
10 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/structured-data/review-script.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script'
2 |
3 | import { createScript } from '../utils'
4 | import { ReviewScriptProps } from '../types'
5 |
6 | export function ReviewScript({ reviews }: ReviewScriptProps) {
7 | const reviewScript = reviews.map((review) => createScript(review, 'Review'))
8 |
9 | return (
10 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/packages/meta-tags/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './schema'
2 | export * from './structured-data-script-props'
3 |
--------------------------------------------------------------------------------
/packages/meta-tags/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/meta-tags/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/meta-tags/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/middlewares/src/chain.ts:
--------------------------------------------------------------------------------
1 | import { NextMiddleware, NextResponse } from 'next/server'
2 |
3 | import { MiddlewareFactory } from './types'
4 |
5 | export function chain(
6 | functions: MiddlewareFactory[],
7 | index = 0,
8 | ): NextMiddleware {
9 | const current = functions[index]
10 |
11 | if (current) {
12 | const next = chain(functions, index + 1)
13 | return current(next)
14 | }
15 |
16 | return () => {
17 | return NextResponse.next()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/middlewares/src/refresh-session/index.ts:
--------------------------------------------------------------------------------
1 | export { refreshSessionMiddleware } from './next-14'
2 | export { refreshSessionMiddleware as refreshSessionMiddlewareNext13 } from './next-13'
3 |
--------------------------------------------------------------------------------
/packages/middlewares/src/types.ts:
--------------------------------------------------------------------------------
1 | import { NextMiddleware } from 'next/server'
2 |
3 | export type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware
4 |
--------------------------------------------------------------------------------
/packages/middlewares/src/utils/get-domain.ts:
--------------------------------------------------------------------------------
1 | import { parseUrl } from '@titicaca/view-utilities'
2 | import { NextRequest } from 'next/server'
3 |
4 | export function getDomain(request: NextRequest) {
5 | const hostFromRequest = request.headers.get('host')
6 | const isLocalhost = hostFromRequest?.split(':')[0] === 'localhost'
7 | const { host } = parseUrl(process.env.NEXT_PUBLIC_WEB_URL_BASE)
8 |
9 | return isLocalhost ? 'localhost' : `.${host}`
10 | }
11 |
--------------------------------------------------------------------------------
/packages/middlewares/src/utils/get-triple-app.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest } from 'next/server'
2 | import { clientAppRegex } from '@titicaca/triple-web-utils'
3 |
4 | export function getIsTripleApp(request: NextRequest) {
5 | const userAgent = request.headers.get('User-Agent')
6 | const tripleAppMetadata = userAgent ? clientAppRegex.exec(userAgent) : null
7 |
8 | return !!tripleAppMetadata
9 | }
10 |
--------------------------------------------------------------------------------
/packages/middlewares/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/middlewares/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/middlewares/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/react-hooks/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-body-scroll-lock'
2 | export * from './use-debounce'
3 | export * from './use-error-handler'
4 | export * from './use-fetch'
5 | export * from './use-interval'
6 | export * from './use-local-storage'
7 | export * from './use-lottie'
8 | export * from './use-scroll-to-anchor'
9 | export * from './use-scroll-to-element'
10 | export * from './use-session-storage'
11 | export * from './use-visibility-change'
12 |
--------------------------------------------------------------------------------
/packages/react-hooks/src/use-error-handler.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 |
3 | /**
4 | * 콜백 함수에서 발생한 에러를 React ErrorBoundary가 잡을 수 있도록 해주는 함수
5 | */
6 | export function useErrorHandler() {
7 | const [error, setError] = useState(null)
8 |
9 | if (error) {
10 | throw error
11 | }
12 |
13 | return setError
14 | }
15 |
--------------------------------------------------------------------------------
/packages/react-hooks/src/use-visibility-change.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 |
3 | export function useVisibilityChange(
4 | onChange: (visible: boolean) => void,
5 | deps: unknown[] = [],
6 | ) {
7 | useEffect(() => {
8 | function handleChange() {
9 | onChange(!document.hidden)
10 | }
11 |
12 | document.addEventListener('visibilitychange', handleChange)
13 |
14 | return () => {
15 | document.removeEventListener('visibilitychange', handleChange)
16 | }
17 | }, deps) // eslint-disable-line react-hooks/exhaustive-deps
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react-hooks/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react-hooks/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/react-hooks/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/router/src/common/add-web-url-base.ts:
--------------------------------------------------------------------------------
1 | import { useEnv } from '@titicaca/triple-web'
2 | import { generateUrl, parseUrl } from '@titicaca/view-utilities'
3 |
4 | export function useWebUrlBaseAdder() {
5 | const { webUrlBase } = useEnv()
6 |
7 | const addWebUrlBaseToHref = (href: string) => {
8 | const { scheme, host } = parseUrl(webUrlBase)
9 |
10 | if (href === '/') {
11 | return webUrlBase
12 | }
13 |
14 | return generateUrl({ scheme, host }, href)
15 | }
16 |
17 | return addWebUrlBaseToHref
18 | }
19 |
--------------------------------------------------------------------------------
/packages/router/src/common/default-router.ts:
--------------------------------------------------------------------------------
1 | import { ANCHOR_TARGET_MAP, TargetProps } from './target'
2 | import { HrefProps } from './types'
3 |
4 | export default function useDefaultRouter() {
5 | const defaultRouter = ({ href, target }: HrefProps & TargetProps) => {
6 | const windowTarget = ANCHOR_TARGET_MAP[target]
7 | window.open(
8 | href,
9 | windowTarget,
10 | windowTarget === '_blank' ? 'noopener' : undefined,
11 | )
12 | }
13 |
14 | return defaultRouter
15 | }
16 |
--------------------------------------------------------------------------------
/packages/router/src/common/target.ts:
--------------------------------------------------------------------------------
1 | export type TargetType = 'current' | 'new' | 'browser'
2 |
3 | export interface TargetProps {
4 | /**
5 | * 페이지를 이동할 목표를 설정합니다.
6 | * `current`, `new`, `browser` 세 가지를 사용할 수 있습니다.
7 | * 각각 현재 창, 새 창(새 웹뷰), 기본 브라우저(앱에서만 작동)를 의미합니다.
8 | */
9 | target: TargetType
10 | }
11 |
12 | export const ANCHOR_TARGET_MAP: {
13 | [key in TargetType]: string
14 | } = {
15 | current: '_self',
16 | new: '_blank',
17 | browser: '_blank',
18 | }
19 |
--------------------------------------------------------------------------------
/packages/router/src/external/index.ts:
--------------------------------------------------------------------------------
1 | export { ExternalLink } from './link'
2 | export { default as useExternalRouter } from './hook'
3 |
--------------------------------------------------------------------------------
/packages/router/src/external/utils.ts:
--------------------------------------------------------------------------------
1 | import { parseUrl } from '@titicaca/view-utilities'
2 |
3 | export function checkHrefIsAbsoluteUrl(href: string): boolean {
4 | const { host } = parseUrl(href)
5 | return !!host
6 | }
7 |
--------------------------------------------------------------------------------
/packages/router/src/href-to-props/index.ts:
--------------------------------------------------------------------------------
1 | export { useHrefToProps } from './use-href-to-props'
2 |
--------------------------------------------------------------------------------
/packages/router/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './links'
2 | export * from './local'
3 | export * from './external'
4 | export * from './navigate'
5 | export { useHrefToProps } from './href-to-props'
6 |
--------------------------------------------------------------------------------
/packages/router/src/links/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-make-inlink'
2 | export * from './use-make-outlink'
3 | export * from './use-open-inlink'
4 | export * from './use-open-native-link'
5 | export * from './use-open-outlink'
6 |
--------------------------------------------------------------------------------
/packages/router/src/links/use-open-inlink.ts:
--------------------------------------------------------------------------------
1 | import { useMakeInlink, MakeInlinkOptions } from './use-make-inlink'
2 |
3 | export type OpenInlinkOptions = MakeInlinkOptions
4 |
5 | export function useOpenInlink() {
6 | const makeInlink = useMakeInlink()
7 |
8 | const openInlink = (
9 | /**
10 | * Inlink로 만들 relative URL.
11 | */
12 | path: string,
13 | options?: OpenInlinkOptions,
14 | ) => {
15 | const href = makeInlink(path, options)
16 | window.location.href = href
17 | }
18 |
19 | return openInlink
20 | }
21 |
--------------------------------------------------------------------------------
/packages/router/src/links/use-open-native-link.ts:
--------------------------------------------------------------------------------
1 | import {
2 | useClientApp,
3 | useEnv,
4 | useAppInstallCtaModal,
5 | } from '@titicaca/triple-web'
6 |
7 | export function useOpenNativeLink() {
8 | const app = useClientApp()
9 | const { appUrlScheme } = useEnv()
10 | const { show: showAppInstallCtaModal } = useAppInstallCtaModal()
11 |
12 | const openNativeLink = (
13 | /**
14 | * 딥링크 path.
15 | */
16 | path: string,
17 | ) => {
18 | if (!app) {
19 | return showAppInstallCtaModal()
20 | }
21 |
22 | window.location.href = `${appUrlScheme}://${path}`
23 | }
24 |
25 | return openNativeLink
26 | }
27 |
--------------------------------------------------------------------------------
/packages/router/src/links/use-open-outlink.ts:
--------------------------------------------------------------------------------
1 | import { MakeOutlinkOptions, useMakeOutlink } from './use-make-outlink'
2 |
3 | export type OpenOutlinkOptions = MakeOutlinkOptions
4 |
5 | export function useOpenOutlink() {
6 | const makeOutlink = useMakeOutlink()
7 |
8 | const openOutlink = (
9 | /**
10 | * Outlink로 만들 absolute URL.
11 | */
12 | url: string,
13 | options?: OpenOutlinkOptions,
14 | ) => {
15 | const href = makeOutlink(url, options)
16 | window.location.href = href
17 | }
18 |
19 | return openOutlink
20 | }
21 |
--------------------------------------------------------------------------------
/packages/router/src/local/base-path.ts:
--------------------------------------------------------------------------------
1 | import { parseUrl, generateUrl } from '@titicaca/view-utilities'
2 | import { useRouter } from 'next/router'
3 |
4 | export function useBasePathAdder() {
5 | const { basePath } = useRouter()
6 |
7 | return (href: string): string => addBasePath(href, basePath)
8 | }
9 |
10 | function addBasePath(href: string, basePath: string): string {
11 | const { path, ...rest } = parseUrl(href)
12 | const newPath = path === '/' ? basePath : `${basePath}${path}`
13 |
14 | return generateUrl({ path: newPath, ...rest })
15 | }
16 |
--------------------------------------------------------------------------------
/packages/router/src/local/index.ts:
--------------------------------------------------------------------------------
1 | export { LocalLink } from './link'
2 | export { default as useLocalRouter } from './hook'
3 |
--------------------------------------------------------------------------------
/packages/router/src/navigate/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-navigate'
2 | export * from './use-isomorphic-navigate'
3 |
--------------------------------------------------------------------------------
/packages/router/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/router/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/scroll-to-element/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as scrollToElement } from './scroll-to-element'
2 |
--------------------------------------------------------------------------------
/packages/scroll-to-element/src/scroll-to-element.ts:
--------------------------------------------------------------------------------
1 | import { scroll } from './scroll'
2 | import { calculateScrollOffset } from './scroll-position-calculators'
3 | import { ScrollOptions } from './types'
4 |
5 | export default function scrollToElement(
6 | element: Element,
7 | options: ScrollOptions,
8 | ) {
9 | if (!element) {
10 | return
11 | }
12 |
13 | return scroll({
14 | x: 0,
15 | y: calculateScrollOffset({
16 | element,
17 | offset: options.offset,
18 | alignment: options.align,
19 | }),
20 | options,
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/packages/scroll-to-element/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface ScrollOptions {
2 | offset: number
3 | align?: 'top' | 'middle' | 'bottom'
4 | duration?: number
5 | }
6 |
--------------------------------------------------------------------------------
/packages/scroll-to-element/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/scroll-to-element/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/scroll-to-element/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './hook'
2 | export * from './initialize'
3 | export * from './types'
4 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/src/invoke-cta.ts:
--------------------------------------------------------------------------------
1 | import { WebActionParams } from './types'
2 |
3 | export default async function invokeCta({
4 | url: { path } = {},
5 | options: { navigate, cta } = {},
6 | }: WebActionParams) {
7 | if (path === '/web-action/cta') {
8 | if (cta && navigate) {
9 | navigate(cta)
10 |
11 | return true
12 | }
13 | }
14 |
15 | return false
16 | }
17 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/src/new-window.test.ts:
--------------------------------------------------------------------------------
1 | import newWindow from './new-window'
2 |
3 | test('새창열기 기능을 테스트합니다', () => {
4 | const href = 'https://triple.guide/'
5 | const routeExternally = jest.fn()
6 |
7 | const navigate = () => {}
8 |
9 | newWindow({
10 | url: {
11 | path: '/web-action/new-window',
12 | query: `href=${encodeURIComponent(href)}`,
13 | },
14 | options: { navigate, routeExternally },
15 | })
16 |
17 | expect(routeExternally).toHaveBeenCalledWith({ href, target: 'new' })
18 | })
19 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/src/new-window.ts:
--------------------------------------------------------------------------------
1 | import qs from 'qs'
2 |
3 | import { WebActionParams } from './types'
4 |
5 | export default async function newWindow({
6 | url: { path, query } = {},
7 | options: { routeExternally } = {},
8 | }: WebActionParams) {
9 | if (path === '/web-action/new-window' && query) {
10 | const { href } = qs.parse(query) as {
11 | href?: string
12 | }
13 | if (href && routeExternally) {
14 | routeExternally({ href, target: 'new' })
15 | return true
16 | }
17 | }
18 | return false
19 | }
20 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/src/show-toast.ts:
--------------------------------------------------------------------------------
1 | import qs from 'qs'
2 | import { showToast as nativeShowToast } from '@titicaca/triple-web-to-native-interfaces'
3 |
4 | import { WebActionParams } from './types'
5 |
6 | export default async function showToast({
7 | url: { path, query } = {},
8 | }: WebActionParams) {
9 | if (path === '/web-action/show-toast' && query) {
10 | const { text } = qs.parse(query, { ignoreQueryPrefix: true })
11 |
12 | nativeShowToast(text as string)
13 |
14 | return true
15 | }
16 |
17 | return false
18 | }
19 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/standard-action-handler/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/tds-theme/src/index.ts:
--------------------------------------------------------------------------------
1 | import './styled'
2 |
3 | export * from './theme'
4 | export * from './global-style'
5 |
--------------------------------------------------------------------------------
/packages/tds-theme/src/styled.ts:
--------------------------------------------------------------------------------
1 | import { CSSProp } from 'styled-components'
2 |
3 | import type { Theme } from './theme/types'
4 |
5 | declare module 'styled-components' {
6 | export interface DefaultTheme extends Theme {}
7 | }
8 |
9 | declare module 'react' {
10 | interface Attributes {
11 | css?: CSSProp
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/tds-theme/src/theme/default.ts:
--------------------------------------------------------------------------------
1 | import { colors } from '../foundations/colors'
2 |
3 | export const defaultTheme = {
4 | colors,
5 | }
6 |
--------------------------------------------------------------------------------
/packages/tds-theme/src/theme/index.ts:
--------------------------------------------------------------------------------
1 | export * from './default'
2 | export * from './types'
3 |
--------------------------------------------------------------------------------
/packages/tds-theme/src/theme/types.ts:
--------------------------------------------------------------------------------
1 | import { defaultTheme } from './default'
2 |
3 | export type Theme = typeof defaultTheme
4 |
--------------------------------------------------------------------------------
/packages/tds-theme/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-theme/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tds-theme/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/accordion/accordion-content.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 |
3 | import { Container } from '../container'
4 |
5 | import { useAccordion } from './accordion-context'
6 |
7 | type AccordionContentProps = PropsWithChildren
8 |
9 | export const AccordionContent = ({
10 | children,
11 | ...props
12 | }: AccordionContentProps) => {
13 | const { active, contentId } = useAccordion()
14 |
15 | if (!active) {
16 | return null
17 | }
18 |
19 | return (
20 |
21 | {children}
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/accordion/accordion-context.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 |
3 | export interface AccordionContextValue {
4 | contentId: string
5 | foldedId: string
6 | active: boolean
7 | onActiveChange: () => void
8 | }
9 |
10 | export const AccordionContext = createContext<
11 | AccordionContextValue | undefined
12 | >(undefined)
13 |
14 | export function useAccordion() {
15 | const context = useContext(AccordionContext)
16 | if (!context) {
17 | throw new Error()
18 | }
19 | return context
20 | }
21 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/accordion/index.ts:
--------------------------------------------------------------------------------
1 | export * from './accordion-content'
2 | export * from './accordion-folded'
3 | export * from './accordion-title'
4 | export * from './accordion'
5 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/action-sheet-select/index.ts:
--------------------------------------------------------------------------------
1 | export * from './action-sheet-select-button'
2 | export * from './action-sheet-select-option'
3 | export * from './action-sheet-select-options'
4 | export * from './action-sheet-select'
5 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/action-sheet/action-sheet-context.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 |
3 | export interface ActionSheetContextValue {
4 | open: boolean
5 | onClose?: () => void
6 | }
7 |
8 | export const ActionSheetContext = createContext<
9 | ActionSheetContextValue | undefined
10 | >(undefined)
11 |
12 | export function useActionSheet() {
13 | const context = useContext(ActionSheetContext)
14 | if (!context) {
15 | throw new Error()
16 | }
17 | return context
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/action-sheet/constants.tsx:
--------------------------------------------------------------------------------
1 | export const TRANSITION_DURATION = 120
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/action-sheet/index.ts:
--------------------------------------------------------------------------------
1 | export * from './action-sheet-body'
2 | export * from './action-sheet-item'
3 | export * from './action-sheet'
4 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/alert/index.ts:
--------------------------------------------------------------------------------
1 | export * from './alert'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/button/index.ts:
--------------------------------------------------------------------------------
1 | export * from './button-base'
2 | export * from './button-container'
3 | export * from './button-group'
4 | export * from './button-icon'
5 | export * from './button'
6 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/button/types.ts:
--------------------------------------------------------------------------------
1 | export type ButtonSize = 'tiny' | 'small' | 'large'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/carousel/index.ts:
--------------------------------------------------------------------------------
1 | export * from './carousel'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/checkbox-group/checkbox-group-context.tsx:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | export interface CheckboxGroupContextValue {
4 | descriptionId: string
5 | errorId: string
6 | isDisabled: boolean
7 | isError: boolean
8 | isFocused: boolean
9 | isRequired: boolean
10 | name?: string
11 | value: string[]
12 | onChange?: (value: string[]) => void
13 | }
14 |
15 | export const CheckboxGroupContext = createContext<
16 | CheckboxGroupContextValue | undefined
17 | >(undefined)
18 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/checkbox-group/index.ts:
--------------------------------------------------------------------------------
1 | export * from './checkbox-group-context'
2 | export * from './checkbox-group'
3 | export * from './use-checkbox-group'
4 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/checkbox-group/use-checkbox-group.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { CheckboxGroupContext } from './checkbox-group-context'
4 |
5 | export function useCheckboxGroup() {
6 | const context = useContext(CheckboxGroupContext)
7 | if (!context) {
8 | throw new Error('CheckboxGroupContext가 없습니다.')
9 | }
10 | return context
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/checkbox/index.ts:
--------------------------------------------------------------------------------
1 | export * from './checkbox-base'
2 | export * from './checkbox'
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/confirm-selector/confirm-selector.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { StoryObj, Meta } from '@storybook/react'
2 |
3 | import { ConfirmSelector } from './confirm-selector'
4 |
5 | const meta: Meta = {
6 | title: 'tds-ui (Form) / ConfirmSelector',
7 | component: ConfirmSelector,
8 | }
9 |
10 | export default meta
11 |
12 | export const Default: StoryObj = {
13 | args: {
14 | children: '이용약관 동의',
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/confirm-selector/index.ts:
--------------------------------------------------------------------------------
1 | export * from './confirm-selector'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/confirm/index.ts:
--------------------------------------------------------------------------------
1 | export * from './confirm'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/container/index.ts:
--------------------------------------------------------------------------------
1 | export * from './container'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/content-elements/index.ts:
--------------------------------------------------------------------------------
1 | export * from './content-elements'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/drawer-button/index.ts:
--------------------------------------------------------------------------------
1 | export * from './drawer-button'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/drawer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './drawer'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/fieldset/fieldset-context.tsx:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | export interface FieldsetContextValue {
4 | isDisabled: boolean
5 | isRequired: boolean
6 | }
7 |
8 | export const FieldsetContext = createContext(
9 | undefined,
10 | )
11 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/fieldset/index.ts:
--------------------------------------------------------------------------------
1 | export * from './fieldset-context'
2 | export * from './fieldset-legend'
3 | export * from './fieldset'
4 | export * from './use-fieldset'
5 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/fieldset/use-fieldset.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { FieldsetContext } from './fieldset-context'
4 |
5 | export function useFieldset() {
6 | const context = useContext(FieldsetContext)
7 | if (!context) {
8 | throw new Error('FieldsetContext가 없습니다.')
9 | }
10 | return context
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/flex-box/index.ts:
--------------------------------------------------------------------------------
1 | export * from './flex-box'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/form-field/form-field-error.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 |
3 | import { Container } from '../container'
4 | import { Text } from '../text'
5 |
6 | import { useFormField } from './form-field-context'
7 |
8 | export type FormFieldErrorProps = PropsWithChildren
9 |
10 | export const FormFieldError = ({ children }: FormFieldErrorProps) => {
11 | const formField = useFormField()
12 |
13 | return (
14 |
15 |
16 | {children}
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/form-field/index.ts:
--------------------------------------------------------------------------------
1 | export * from './form-field-context'
2 | export * from './form-field-error'
3 | export * from './form-field-help'
4 | export * from './form-field-label'
5 | export * from './form-field'
6 | export * from './use-form-field-state'
7 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/gender-selector/index.ts:
--------------------------------------------------------------------------------
1 | export * from './gender-selector'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/hr/index.ts:
--------------------------------------------------------------------------------
1 | export * from './hr'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/icon/index.ts:
--------------------------------------------------------------------------------
1 | export * from './icon'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/image/index.ts:
--------------------------------------------------------------------------------
1 | export * from './circular'
2 | export * from './fixed-dimensions-frame'
3 | export * from './fixed-ratio-frame'
4 | export * from './image'
5 | export * from './img'
6 | export * from './link-indicator'
7 | export * from './optimized-img'
8 | export * from './overlay'
9 | export * from './placeholder'
10 | export * from './source-url'
11 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/image/link-indicator.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 |
3 | import { Icon } from '../icon'
4 |
5 | const IconContainer = styled.div`
6 | position: absolute;
7 | top: 30px;
8 | right: 10px;
9 | width: 20px;
10 | height: 20px;
11 | `
12 |
13 | export function ImageLinkIndicator() {
14 | return (
15 |
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/image/source-url.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 | import { styled } from 'styled-components'
3 |
4 | const SourceUrlContainer = styled.div`
5 | position: absolute;
6 | right: 10px;
7 | bottom: 10px;
8 | max-width: 150px;
9 | overflow: hidden;
10 | white-space: nowrap;
11 | text-overflow: ellipsis;
12 | font-size: 9px;
13 | line-height: 1.2;
14 | color: rgba(255, 255, 255, 0.9);
15 | z-index: 1;
16 | `
17 |
18 | export function ImageSourceUrl({ children }: PropsWithChildren) {
19 | return {children}
20 | }
21 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/input/index.ts:
--------------------------------------------------------------------------------
1 | export * from './input'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/label/index.ts:
--------------------------------------------------------------------------------
1 | export * from './label'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/list/index.ts:
--------------------------------------------------------------------------------
1 | export * from './list'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/long-clickable/index.ts:
--------------------------------------------------------------------------------
1 | export * from './long-clickable'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/modal/index.ts:
--------------------------------------------------------------------------------
1 | export * from './modal-action'
2 | export * from './modal-actions'
3 | export * from './modal-body'
4 | export * from './modal-title'
5 | export * from './modal'
6 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/modal/modal-body.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 | import { css } from 'styled-components'
3 |
4 | import { Container } from '../container'
5 |
6 | export const ModalBody = ({ children, ...props }: PropsWithChildren) => {
7 | return (
8 |
14 | {children}
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/modal/modal-context.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 |
3 | export interface ModalContextValue {
4 | open: boolean
5 | labelId: string
6 | descriptionId: string
7 | onClose?: () => void
8 | }
9 |
10 | export const ModalContext = createContext(
11 | undefined,
12 | )
13 |
14 | export function useModal() {
15 | const context = useContext(ModalContext)
16 | if (!context) {
17 | throw new Error()
18 | }
19 | return context
20 | }
21 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/modal/modal-description.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 |
3 | import { Text } from '../text'
4 |
5 | import { useModal } from './modal-context'
6 |
7 | export type ModalDescriptionProps = PropsWithChildren
8 |
9 | export const ModalDescription = ({ children }: ModalDescriptionProps) => {
10 | const { descriptionId } = useModal()
11 |
12 | return (
13 |
14 | {children}
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/modal/modal-title.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 | import { styled } from 'styled-components'
3 |
4 | import { Text } from '../text'
5 |
6 | import { useModal } from './modal-context'
7 |
8 | const StyledText = styled(Text)`
9 | margin-bottom: 10px;
10 | `
11 |
12 | export type ModalTitleProps = PropsWithChildren
13 |
14 | export const ModalTitle = ({ children }: ModalTitleProps) => {
15 | const { labelId } = useModal()
16 |
17 | return (
18 |
19 | {children}
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/navbar/index.ts:
--------------------------------------------------------------------------------
1 | export * from './navbar'
2 | export * from './search-navbar'
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/numeric-spinner/index.ts:
--------------------------------------------------------------------------------
1 | export * from './numeric-spinner-base'
2 | export * from './numeric-spinner'
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/popup/index.ts:
--------------------------------------------------------------------------------
1 | export * from './popup'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/radio-group/index.ts:
--------------------------------------------------------------------------------
1 | export * from './radio-group-context'
2 | export * from './radio-group'
3 | export * from './use-radio-group'
4 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/radio-group/radio-group-context.tsx:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | export interface RadioGroupContextValue {
4 | descriptionId: string
5 | errorId: string
6 | isDisabled: boolean
7 | isError: boolean
8 | isFocused: boolean
9 | isRequired: boolean
10 | name?: string
11 | value?: string
12 | onChange?: (value: string) => void
13 | }
14 |
15 | export const RadioGroupContext = createContext<
16 | RadioGroupContextValue | undefined
17 | >(undefined)
18 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/radio-group/radio-group-error.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 |
3 | import { Container } from '../container'
4 | import { Text } from '../text'
5 |
6 | import { useRadioGroup } from './use-radio-group'
7 |
8 | export type RadioGroupErrorProps = PropsWithChildren
9 |
10 | export const RadioGroupError = ({ children }: RadioGroupErrorProps) => {
11 | const radioGroup = useRadioGroup()
12 |
13 | return (
14 |
15 |
16 | {children}
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/radio-group/use-radio-group.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { RadioGroupContext } from './radio-group-context'
4 |
5 | export function useRadioGroup() {
6 | const context = useContext(RadioGroupContext)
7 | if (!context) {
8 | throw new Error('RadioGroupContext가 없습니다.')
9 | }
10 | return context
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/radio/index.ts:
--------------------------------------------------------------------------------
1 | export * from './radio-base'
2 | export * from './radio'
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/rating/index.ts:
--------------------------------------------------------------------------------
1 | export * from './rating'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/responsive/index.ts:
--------------------------------------------------------------------------------
1 | export * from './responsive'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/section/index.ts:
--------------------------------------------------------------------------------
1 | export * from './section'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/segment/index.ts:
--------------------------------------------------------------------------------
1 | export * from './segment'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/select/index.ts:
--------------------------------------------------------------------------------
1 | export * from './select'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/skeleton/index.ts:
--------------------------------------------------------------------------------
1 | export * from './skeleton'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/slider/index.ts:
--------------------------------------------------------------------------------
1 | export * from './single-slider'
2 | export * from './range-slider'
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/slider/types.ts:
--------------------------------------------------------------------------------
1 | export type SliderValue = readonly number[]
2 |
3 | export type ValueTransformer = (x: number) => number
4 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/spinner/index.ts:
--------------------------------------------------------------------------------
1 | export * from './rolling-spinner'
2 | export * from './spinner'
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/stack/index.ts:
--------------------------------------------------------------------------------
1 | export * from './stack'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/stack/stack.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 |
3 | import { Stack } from './stack'
4 |
5 | it('should override style with css prop', () => {
6 | render(
7 |
8 | stack
9 | ,
10 | )
11 |
12 | const element = screen.getByText('stack')
13 |
14 | expect(element).toHaveStyleRule('position', 'fixed')
15 | })
16 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/sticky-header/index.ts:
--------------------------------------------------------------------------------
1 | export * from './sticky-header'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/sticky-header/sticky-header.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 |
3 | import { StickyHeader } from './sticky-header'
4 |
5 | it('should override style with css prop', () => {
6 | render()
7 |
8 | const element = screen.getByRole('banner')
9 |
10 | expect(element).toHaveStyleRule('top', '10px')
11 | })
12 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/table/index.ts:
--------------------------------------------------------------------------------
1 | export * from './table'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/tabs/basic-tab-list.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 |
3 | import { TabListBase, TabListBaseProps } from './tab-list-base'
4 |
5 | const StyledTabListBase = styled(TabListBase)`
6 | display: flex;
7 | background-color: var(--color-brightGray);
8 | border-radius: 4px;
9 | padding: 2px;
10 | `
11 |
12 | export const BasicTabList = ({ children, ...props }: TabListBaseProps) => {
13 | return {children}
14 | }
15 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/tabs/index.ts:
--------------------------------------------------------------------------------
1 | export * from './tab-base'
2 | export * from './tab-list-base'
3 | export * from './tab-list'
4 | export * from './tab-panel'
5 | export * from './tab'
6 | export * from './tabs-context'
7 | export * from './tabs'
8 | export * from './types'
9 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/tabs/types.ts:
--------------------------------------------------------------------------------
1 | export type TabVariant = 'basic' | 'pointing' | 'rounded'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/tag/index.ts:
--------------------------------------------------------------------------------
1 | export * from './tag'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/text/index.ts:
--------------------------------------------------------------------------------
1 | export * from './text'
2 | export * from './typography'
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/textarea/index.ts:
--------------------------------------------------------------------------------
1 | export * from './textarea'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/tooltip/index.ts:
--------------------------------------------------------------------------------
1 | export * from './tooltip'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/video/index.ts:
--------------------------------------------------------------------------------
1 | export * from './video-element'
2 | export * from './video-frame'
3 | export * from './video'
4 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/video/utils.tsx:
--------------------------------------------------------------------------------
1 | export function formatTime(totalSeconds: number) {
2 | const minutes = pad2(String(Math.floor(totalSeconds / 60)))
3 | const seconds = pad2(String(totalSeconds % 60))
4 |
5 | return `${minutes}:${seconds}`
6 | }
7 |
8 | function pad2(original: string) {
9 | if (original.length === 0) {
10 | return '00'
11 | } else if (original.length === 1) {
12 | return `0${original}`
13 | }
14 |
15 | return original
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/visually-hidden/index.ts:
--------------------------------------------------------------------------------
1 | export * from './visually-hidden'
2 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/components/visually-hidden/visually-hidden.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 |
3 | import { VisuallyHidden } from './visually-hidden'
4 |
5 | test('스크린 리더가 읽을 수 있습니다.', () => {
6 | render(Visually Hidden)
7 |
8 | expect(screen.getByText('Visually Hidden')).toBeInTheDocument()
9 | })
10 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components'
2 | export * from './mixins'
3 | export * from './commons'
4 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/border-radius.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export interface BorderRadiusMixinProps {
4 | borderRadius?: number
5 | }
6 |
7 | /**
8 | * border-radius를 지정하는 mixin
9 | * 자식 엘리먼트가 border-radius를 무시하지 않도록 overflow: hidden이 추가됩니다.
10 | * 또한, 관련한 safari 버그를 우회하는 workaround도 포함합니다.
11 | * @param borderRadiusMixin
12 | */
13 | export const borderRadiusMixin = ({ borderRadius }: BorderRadiusMixinProps) =>
14 | borderRadius &&
15 | css`
16 | overflow: hidden;
17 | mask-image: radial-gradient(white, black);
18 | border-radius: ${borderRadius}px;
19 | `
20 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/centered.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export interface CenteredMixinProps {
4 | centered?: boolean
5 | }
6 |
7 | export const centeredMixin = ({ centered }: CenteredMixinProps) =>
8 | centered
9 | ? css`
10 | margin-left: auto;
11 | margin-right: auto;
12 | `
13 | : undefined
14 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/clearing.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export interface ClearingMixinProps {
4 | clearing?: boolean
5 | }
6 |
7 | export const clearingMixin = ({ clearing }: ClearingMixinProps) =>
8 | clearing
9 | ? css`
10 | &::after {
11 | content: '';
12 | display: block;
13 | clear: both;
14 | }
15 | `
16 | : undefined
17 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/ellipsis.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export interface EllipsisMixinProps {
4 | ellipsis?: boolean
5 | }
6 |
7 | export const ellipsisMixin = ({ ellipsis }: EllipsisMixinProps) =>
8 | ellipsis
9 | ? css`
10 | white-space: nowrap;
11 | text-overflow: ellipsis;
12 | overflow: hidden;
13 | `
14 | : undefined
15 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/horizontal-scroll.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export interface HorizontalScrollMixinProps {
4 | horizontalScroll?: boolean
5 | }
6 |
7 | export const horizontalScrollMixin = ({
8 | horizontalScroll,
9 | }: HorizontalScrollMixinProps) =>
10 | horizontalScroll
11 | ? css`
12 | white-space: nowrap;
13 | overflow: auto hidden;
14 | `
15 | : undefined
16 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/index.ts:
--------------------------------------------------------------------------------
1 | export * from './border-radius'
2 | export * from './box'
3 | export * from './centered'
4 | export * from './clearing'
5 | export * from './ellipsis'
6 | export * from './horizontal-scroll'
7 | export * from './layering'
8 | export * from './margin-padding'
9 | export * from './max-lines'
10 | export * from './positioning'
11 | export * from './safe-area'
12 | export * from './text-style'
13 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/layering.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export interface LayeringMixinProps {
4 | zTier?: number
5 | zIndex?: number
6 | }
7 |
8 | export const layeringMixin =
9 | (defaultTier: number) =>
10 | ({ zIndex = defaultTier, zTier = 0 }: LayeringMixinProps) => css`
11 | z-index: ${zTier * 100 + zIndex};
12 | `
13 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/max-lines.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export interface MaxLinesMixinProps {
4 | maxLines?: number
5 | }
6 |
7 | export const maxLinesMixin = ({ maxLines }: MaxLinesMixinProps) =>
8 | maxLines
9 | ? css`
10 | /* stylelint-disable-next-line value-no-vendor-prefix */
11 | display: -webkit-box;
12 | -webkit-box-orient: vertical;
13 | -webkit-line-clamp: ${maxLines};
14 | text-overflow: ellipsis;
15 | overflow: hidden;
16 | white-space: pre-line;
17 | `
18 | : undefined
19 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/mixins/safe-area.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | import { MarginPadding } from '../commons'
4 | import { unit } from '../utils/unit'
5 |
6 | export interface SafeAreaInsetMixinProps {
7 | padding?: MarginPadding
8 | }
9 |
10 | export const safeAreaInsetMixin = ({ padding }: SafeAreaInsetMixinProps) => {
11 | const paddingBottom = unit(padding?.bottom || '0px')
12 |
13 | return css`
14 | @supports (padding: env(safe-area-inset-bottom)) {
15 | padding-bottom: calc(env(safe-area-inset-bottom) + ${paddingBottom});
16 | }
17 | `
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/utils/merge-refs.ts:
--------------------------------------------------------------------------------
1 | import type * as React from 'react'
2 |
3 | export function mergeRefs(
4 | refs: Array | React.LegacyRef | undefined>,
5 | ): React.RefCallback {
6 | return (value) => {
7 | refs.forEach((ref) => {
8 | if (typeof ref === 'function') {
9 | ref(value)
10 | } else if (ref != null) {
11 | ;(ref as React.MutableRefObject).current = value
12 | }
13 | })
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/utils/should-forward-prop.ts:
--------------------------------------------------------------------------------
1 | import isPropValid from '@emotion/is-prop-valid'
2 | import { ShouldForwardProp } from 'styled-components'
3 |
4 | // styled-components v5의 기본 동작을 구현합니다.
5 | export const shouldForwardProp: ShouldForwardProp<'web'> = (
6 | propName,
7 | target,
8 | ) => {
9 | if (typeof target === 'string') {
10 | // HTML 요소인 경우, 유효한 HTML 속성이면 prop을 전달합니다.
11 | return isPropValid(propName)
12 | }
13 | // 다른 요소들에 대해서는 모든 props를 전달합니다.
14 | return true
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tds-ui/src/utils/unit.ts:
--------------------------------------------------------------------------------
1 | export const unit = (value: number | string, suffix = 'px') =>
2 | typeof value === 'string' ? value : value !== 0 ? `${value}${suffix}` : value
3 |
--------------------------------------------------------------------------------
/packages/tds-ui/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tds-ui/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/.eslintignore:
--------------------------------------------------------------------------------
1 | **/generated.ts
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/.prettierignore:
--------------------------------------------------------------------------------
1 | **/generated.ts
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/.stylelintignore:
--------------------------------------------------------------------------------
1 | **/generated.ts
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/ad-banners/index.ts:
--------------------------------------------------------------------------------
1 | export * from './list-top-banners'
2 | export * from './content-details-banner'
3 | export { ListDirection } from './typing'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/ad-banners/list-section.ts:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 | import { Section } from '@titicaca/tds-ui'
3 |
4 | const ListSection = styled(Section)`
5 | @media (min-width: 600px) {
6 | display: none;
7 | }
8 | `
9 |
10 | export default ListSection
11 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/ad-banners/typing.ts:
--------------------------------------------------------------------------------
1 | export interface Banner {
2 | id: string
3 | desc: string
4 | image: string
5 | target: string
6 | }
7 |
8 | export enum ListDirection {
9 | Vertical = 'vertical',
10 | Horizontal = 'horizontal',
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-banner/app-banner.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { AppBanner } from './app-banner'
4 |
5 | export default {
6 | title: 'tds-widget / app-banner / AppBanner',
7 | component: AppBanner,
8 | } as Meta
9 |
10 | export const Basic: StoryObj = {
11 | args: {
12 | title: '트리플 - 해외여행 가이드',
13 | description: '가이드북, 일정짜기, 길찾기, 맛집',
14 | cta: '앱에서 보기',
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-banner/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app-banner'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/banner-cta.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { BannerCta as BannerCTA } from './banner-cta'
4 |
5 | export default {
6 | title: 'tds-widget / app-installation-cta / BannerCTA',
7 | component: BannerCTA,
8 | } as Meta
9 |
10 | // TODO: 서버에 데이터가 없어서 mocking 해야 할 듯
11 | export const Basic: StoryObj = {
12 | args: {
13 | inventoryId: 'app-install-cta-tna-v1',
14 | installUrl: 'https://triple-dev.titicaca-corp.com',
15 | disableTextBanner: true,
16 | },
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/constants.ts:
--------------------------------------------------------------------------------
1 | export enum BannerExitStrategy {
2 | NONE = 'NONE',
3 | CHATBOT_READY = 'CHATBOT_READY',
4 | }
5 |
6 | export const FLOATING_BUTTON_CLOSED_STORAGE_KEY = 'close_install_button'
7 | export const CHATBOT_CLOSED_STORAGE_KEY = 'triple_chatbotad_closed'
8 | export const EVENT_CHATBOT_CTA_READY = 'triple_chatbot_cta_ready'
9 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/floating-button-cta.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { FloatingButtonCta as FloatingButtonCTA } from './floating-button-cta'
4 |
5 | export default {
6 | title: 'tds-widget / app-installation-cta / FloatingButtonCTA',
7 | component: FloatingButtonCTA,
8 | parameters: {
9 | chromatic: {
10 | viewports: [375],
11 | },
12 | },
13 | } as Meta
14 |
15 | export const Basic: StoryObj = {
16 | args: {
17 | appInstallLink: 'https://triple.onelink.me/aZP6/21d43a81',
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/image-banner.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { ImageBanner } from './image-banner'
4 |
5 | export default {
6 | title: 'tds-widget / app-installation-cta / ImageBanner',
7 | component: ImageBanner,
8 | } as Meta
9 |
10 | export const Basic: StoryObj = {
11 | args: {
12 | // TODO: 이미지 추가하면 좋을 것 같은데 어떤 이미지를 써야할지 모르겠음
13 | imgUrl: '',
14 | installUrl: 'https://triple-dev.titicaca-corp.com',
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/index.ts:
--------------------------------------------------------------------------------
1 | export * from './article-card-cta'
2 | export * from './banner-cta'
3 | export * from './chatbot-cta'
4 | export * from './floating-button-cta'
5 | export * from './image-banner'
6 | export * from './text-banner'
7 |
8 | export * from './constants'
9 | export * from './service'
10 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/interfaces.ts:
--------------------------------------------------------------------------------
1 | import { InventoryItemMeta } from '@titicaca/type-definitions'
2 |
3 | export interface CtaProps {
4 | onShow?: (item?: InventoryItemMeta) => void
5 | onClick?: (item?: InventoryItemMeta) => void
6 | onDismiss?: (item?: InventoryItemMeta) => void
7 | }
8 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/service.ts:
--------------------------------------------------------------------------------
1 | import { InventoryItemMeta } from '@titicaca/type-definitions'
2 |
3 | export async function fetchInventoryItems({
4 | inventoryId,
5 | }: {
6 | inventoryId?: string
7 | }): Promise {
8 | const response = await fetch(`/api/inventories/v1/${inventoryId}/items`, {
9 | credentials: 'same-origin',
10 | })
11 |
12 | if (response.ok) {
13 | const { items = [] }: { items: InventoryItemMeta[] } = await response.json()
14 | return items
15 | } else {
16 | return null
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/app-installation-cta/text-banner.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { TextBanner } from './text-banner'
4 |
5 | export default {
6 | title: 'tds-widget / app-installation-cta / TextBanner',
7 | component: TextBanner,
8 | } as Meta
9 |
10 | export const Basic: StoryObj = {
11 | args: {
12 | message: '앱 다운로드시 가이드북 무료',
13 | installUrl: 'https://triple-dev.titicaca-corp.com',
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/author/index.ts:
--------------------------------------------------------------------------------
1 | export * from './author'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble-container/index.ts:
--------------------------------------------------------------------------------
1 | export { default as BubbleContainer } from './bubble-container'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble/constants.ts:
--------------------------------------------------------------------------------
1 | export const ALTERNATIVE_TEXT_MESSAGE = {
2 | blinded: '관리자에 의해 삭제된 메세지입니다.',
3 | deleted: '삭제된 메세지입니다.',
4 | unfriended: '차단한 사용자의 메세지입니다.',
5 | }
6 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble/index.ts:
--------------------------------------------------------------------------------
1 | export { ImageBubble } from './image'
2 | export { TextBubble } from './text'
3 | export { RichBubble } from './rich'
4 | export { ProductBubble } from './product'
5 | export { Badge, ProductName, ProductImage, ProductHr } from './elements'
6 | export { default as BubbleUI } from './bubble-ui'
7 | export { ALTERNATIVE_TEXT_MESSAGE } from './constants'
8 | export * from './nol'
9 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble/item/image.tsx:
--------------------------------------------------------------------------------
1 | import { MouseEventHandler } from 'react'
2 | import { styled } from 'styled-components'
3 |
4 | const PreviewImage = styled.img`
5 | object-fit: cover;
6 | border-radius: 4px;
7 | `
8 |
9 | export default function ImageItem({
10 | src,
11 | onClick,
12 | ...props
13 | }: {
14 | src: string
15 | onClick?: MouseEventHandler
16 | }) {
17 | return
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble/item/index.ts:
--------------------------------------------------------------------------------
1 | export { default as ImageItem } from './image'
2 | export { default as TextItem } from './text'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble/nol/full-text-view/index.ts:
--------------------------------------------------------------------------------
1 | export * from './elements'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble/nol/index.ts:
--------------------------------------------------------------------------------
1 | export * from './full-text-view'
2 | export * from './styles'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/bubble/parent/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './parent-ui'
2 | export type { ParentMessageUIProp } from './parent-ui'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/chat/chat-room-messages/constants.ts:
--------------------------------------------------------------------------------
1 | import { ChatMessageInterface } from '../../types'
2 |
3 | export const DEFAULT_MESSAGE_PROPERTIES: Partial = {
4 | displayTarget: 'all',
5 | }
6 |
7 | export const REACTION_ENABLED_MESSAGE_PROPERTIES: Partial =
8 | {
9 | ...DEFAULT_MESSAGE_PROPERTIES,
10 | reactions: {
11 | thanks: {
12 | count: 0,
13 | haveMine: false,
14 | },
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/chat/chat-room-messages/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | useChatApiService,
3 | useChatMessagesContext,
4 | } from './chat-message-context'
5 | export * from './constants'
6 | export * from './use-unread-messages'
7 | export * from './use-chat-room-messages'
8 | export {
9 | default as ChatRoomMessages,
10 | type ChatRoomMessageInterface,
11 | } from './messages'
12 | export * from './chat-room-messages-provider'
13 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/chat/constants.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_MESSAGE_ID_PREFIX = 'message'
2 |
3 | export const REPLY_BUTTON_DATA_ID = 'reply-button'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/chat/index.ts:
--------------------------------------------------------------------------------
1 | export * from './scroll-context'
2 | export * from './constants'
3 | export * from './chat-scroll-container'
4 | export * from './messages-reducer'
5 | export * from './chat-room-messages'
6 | export * from './room-context'
7 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/expired/index.ts:
--------------------------------------------------------------------------------
1 | export * from './expired'
2 | export {
3 | Button as ExpiredButton,
4 | ButtonIcon as ExpiredButtonIcon,
5 | } from './elements'
6 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/icons/arrow-bottom-16-icon.tsx:
--------------------------------------------------------------------------------
1 | export function ArrowBottom16Icon({
2 | color = '#545457',
3 | ...props
4 | }: {
5 | color?: string
6 | }) {
7 | return (
8 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/icons/arrow-top-icon.tsx:
--------------------------------------------------------------------------------
1 | export default function ArrowTopIcon({
2 | color = '#29292D',
3 | ...props
4 | }: {
5 | color?: string
6 | }) {
7 | return (
8 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/icons/text-full-view-arrow-icon.tsx:
--------------------------------------------------------------------------------
1 | export function TextFullViewArrowIcon({
2 | color = '#545457',
3 | }: {
4 | color?: string
5 | }) {
6 | return (
7 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './bubble'
3 | export * from './bubble-container'
4 | export * from './chat'
5 | export * from './utils'
6 | export * from './navbar'
7 | export * from './input-area'
8 | export { default as Messages } from './messages'
9 | export * from './reservation-info'
10 | export * from './expired'
11 | export * from './preview'
12 | export * from './list'
13 | export * from './scroll-buttons-area'
14 | export * from './nol-theme-provider'
15 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/input-area/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './input-area-ui'
2 | export {
3 | NolInputAreaUI,
4 | INPUT_AREA_HEIGHT as NOL_INPUT_AREA_HEIGHT,
5 | } from './nol-input-area-ui'
6 | export { type NolInputAreaUIProps } from './nol-input-area-ui/types'
7 | export * from './nol-input-area-ui/use-input-resize-observer'
8 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/input-area/utils.ts:
--------------------------------------------------------------------------------
1 | import { SyntheticEvent } from 'react'
2 |
3 | export function textAreaAutoResize(e: SyntheticEvent, maxHeight: number) {
4 | const textareaElement = e.target as HTMLTextAreaElement
5 | textareaElement.style.height = ''
6 | textareaElement.style.height =
7 | Math.min(textareaElement.scrollHeight, maxHeight) + 'px'
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/list/index.ts:
--------------------------------------------------------------------------------
1 | export * from './hooks'
2 | export * from './reducer'
3 | export * from './types'
4 | export * from './utils'
5 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/messages/date-divider.tsx:
--------------------------------------------------------------------------------
1 | import { Text } from '@titicaca/tds-ui'
2 | import { format } from 'date-fns'
3 |
4 | export function DateDivider({ date, ...props }: { date: Date }) {
5 | return (
6 |
13 | {format(date, 'yyyy년 MM월 dd일')}
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/nol-theme-provider/index.ts:
--------------------------------------------------------------------------------
1 | export * from './nol-theme-provider'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/nol-theme-provider/nol-theme-provider.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 | import { ThemeProvider } from 'styled-components'
3 | import { DefaultTheme } from 'styled-components/dist/types'
4 |
5 | import { convertKeysToCamelCase } from './converter'
6 |
7 | export function NolThemeProvider({
8 | children,
9 | theme,
10 | }: {
11 | children: ReactNode
12 | theme: T
13 | }) {
14 | return (
15 |
18 | {children}
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/preview/index.ts:
--------------------------------------------------------------------------------
1 | export * from './elements'
2 | export * from './preview'
3 | export * from './utils'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/reservation-info/index.ts:
--------------------------------------------------------------------------------
1 | export * from './reservation-info'
2 | export { Label as ChatReservationInfoLabel } from './elements'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/types/base.ts:
--------------------------------------------------------------------------------
1 | export type ValueOf = T[keyof T]
2 |
3 | export const ChatChannelEvents = {
4 | REFRESH: 'refresh',
5 | UNREAD: 'unread',
6 | SEND: 'send',
7 | JOIN: 'join',
8 | } as const
9 |
10 | export type ChatChannelEventsType = ValueOf
11 |
12 | export interface ChatChannelInfo {
13 | channel: string
14 | events: Record
15 | needAuth: boolean
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './base'
2 | export * from './image'
3 | export * from './message'
4 | export * from './room'
5 | export * from './ui'
6 | export * from './unread'
7 | export * from './user'
8 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './image'
2 | export { default as useATagNavigator } from './a-tag-navigator'
3 | export * from './user'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/chat/utils/profile.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_MAX_USERNAME_LENGTH = 10
2 |
3 | export function formatUsername({
4 | name,
5 | unregistered = false,
6 | maxLength,
7 | }: {
8 | name: string
9 | unregistered?: boolean | null
10 | maxLength?: number
11 | }) {
12 | if (unregistered) {
13 | return '***'
14 | }
15 |
16 | if (maxLength) {
17 | return name.length > maxLength ? `${name.slice(0, maxLength)}⋯` : name
18 | }
19 |
20 | return name
21 | }
22 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/content-sharing/content-sharing.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { ContentSharing } from './content-sharing'
4 |
5 | export default {
6 | title: 'tds-widget / content-sharing / ContentSharing',
7 | component: ContentSharing,
8 | } as Meta
9 |
10 | export const Basic: StoryObj = {
11 | args: {
12 | label: '친구들과 여행 정보를 공유하세요',
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/content-sharing/index.ts:
--------------------------------------------------------------------------------
1 | export * from './content-sharing'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/date-picker/constants.ts:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 | import MomentLocaleUtils from 'react-day-picker/moment'
3 |
4 | import { formatMonthTitle } from './utils'
5 |
6 | export const LOCALE = 'ko'
7 | export const WEEKDAY_SHORT_LABEL = moment.localeData('ko').weekdaysShort()
8 | export const LOCALE_UTILS = { ...MomentLocaleUtils, formatMonthTitle }
9 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/date-picker/index.ts:
--------------------------------------------------------------------------------
1 | export * from './day-picker'
2 | export * from './mixins'
3 | export * from './picker-frame'
4 | export * from './range-picker-v2'
5 | export * from './range-picker'
6 | export * from './utils'
7 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/date-picker/range-picker-v2/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './picker-frame'
2 | export * from './range-picker'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/date-picker/service.ts:
--------------------------------------------------------------------------------
1 | import { authGuardedFetchers, RequestOptions } from '@titicaca/fetcher'
2 |
3 | export interface Holiday {
4 | name: string
5 | date: string
6 | type: string
7 | country: string
8 | }
9 |
10 | export async function fetchPublicHolidays(
11 | { from, to }: { from: string; to: string },
12 | options: RequestOptions,
13 | ) {
14 | return authGuardedFetchers.get(
15 | `/api/calendar/holidays/KR/${from}/${to}`,
16 | options,
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/directions-finder/constants.ts:
--------------------------------------------------------------------------------
1 | export const HASH_ASK_TO_LOCALS_POPUP = 'popup.ask-to-locals'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/directions-finder/index.ts:
--------------------------------------------------------------------------------
1 | export * from './direction-buttons'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/flicking-carousel/index.ts:
--------------------------------------------------------------------------------
1 | export * from './flicking-carousel'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/footer/constants.ts:
--------------------------------------------------------------------------------
1 | export const MAX_PHONE_WIDTH = 360
2 | export const DEFAULT_FOOTER_MIN_HEIGHT = 179
3 | export const AWARD_FOOTER_MIN_HEIGHT = 205.5
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/footer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './award-footer'
2 | export * from './default-footer'
3 | export * from './logo-footer'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/footer/logo-footer.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { LogoFooter } from './logo-footer'
4 |
5 | export default {
6 | title: 'tds-widget / footer / LogoFooter',
7 | component: LogoFooter,
8 | } as Meta
9 |
10 | export const Basic: StoryObj = {}
11 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/hub-form/cta.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren, SyntheticEvent } from 'react'
2 | import { Button } from '@titicaca/tds-ui'
3 |
4 | export function Cta({
5 | available,
6 | onSubmit,
7 | children,
8 | }: PropsWithChildren<{
9 | available?: boolean
10 | onSubmit: (e: SyntheticEvent) => void
11 | }>) {
12 | return (
13 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/hub-form/index.ts:
--------------------------------------------------------------------------------
1 | export * from './cell'
2 | export * from './cta'
3 | export * from './hub-form'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/image-carousel/index.ts:
--------------------------------------------------------------------------------
1 | export * from './image-carousel'
2 | export * from './page-label'
3 | export * from './types'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/image-carousel/types.ts:
--------------------------------------------------------------------------------
1 | import { GlobalSizes, ImageMeta } from '@titicaca/type-definitions'
2 |
3 | export interface CarouselImageMeta extends ImageMeta {
4 | size?: GlobalSizes
5 | }
6 |
7 | export interface RendererParams {
8 | currentIndex: number
9 | totalCount: number
10 | }
11 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/image-source/image-source.tsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from '@titicaca/triple-web'
2 |
3 | export interface ImageSourceProps {
4 | sourceUrl?: string
5 | }
6 |
7 | export function ImageSource({ sourceUrl }: ImageSourceProps) {
8 | const t = useTranslation()
9 |
10 | if (!sourceUrl) {
11 | return null
12 | }
13 |
14 | const httpsSchemeRemovedUrl = sourceUrl.replace(/^https?:\/\//, '')
15 |
16 | return (
17 | <>
18 | {t('출처 {{httpsSchemeRemovedUrl}}', {
19 | httpsSchemeRemovedUrl,
20 | })}
21 | >
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/image-source/index.ts:
--------------------------------------------------------------------------------
1 | export * from './image-source'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/image-viewer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './image-viewer'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/listing-filter/index.ts:
--------------------------------------------------------------------------------
1 | export * from './listing-filter'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/listing-filter/listing-filter-expanding-filter-entry.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { ListingFilter } from './listing-filter'
4 |
5 | export default {
6 | title: 'tds-widget / listing-filter / ListingFilter.ExpandingFilterEntry',
7 | component: ListingFilter.ExpandingFilterEntry,
8 | } as Meta
9 |
10 | export const Basic: StoryObj = {
11 | name: '기본 Expanding',
12 | args: {
13 | active: true,
14 | disabled: false,
15 | badge: '0',
16 | children: '성급 및 필터',
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/location-properties/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './location-properties'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/map/index.ts:
--------------------------------------------------------------------------------
1 | export * from './map-view'
2 | export * from './focus-tracker'
3 | export * from './overlay'
4 | export * from './utilities'
5 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/map/overlay/index.ts:
--------------------------------------------------------------------------------
1 | export * from './polyline'
2 | export * from './polygon'
3 | export * from './markers'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/map/overlay/markers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './primary-marker'
2 | export * from './poi-dot-marker'
3 | export * from './flexible-marker'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/map/overlay/markers/primary-marker/index.ts:
--------------------------------------------------------------------------------
1 | export * from './pin-marker'
2 | export * from './circle-marker'
3 | export * from './bubble-marker'
4 | export * from './dot-marker'
5 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/map/overlay/markers/primary-marker/pin-marker/index.ts:
--------------------------------------------------------------------------------
1 | import { PinWithCircleMarker } from './pin-marker'
2 |
3 | export * from './pin-marker'
4 |
5 | /** 공통으로 사용되는 poi 마커 */
6 | export const HotelCircleMarker = PinWithCircleMarker('hotel')
7 | export const AttractionCircleMarker = PinWithCircleMarker('attraction')
8 | export const RestaurantCircleMarker = PinWithCircleMarker('restaurant')
9 | export const TnaCircleMarker = PinWithCircleMarker('tna')
10 | export const FestaCircleMarker = PinWithCircleMarker('festa')
11 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/media/index.ts:
--------------------------------------------------------------------------------
1 | export * from './media'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/nearby-pois/index.ts:
--------------------------------------------------------------------------------
1 | export * from './nearby-pois'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/nearby-pois/types.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ListingAttraction,
3 | ListingRestaurant,
4 | } from '@titicaca/type-definitions'
5 |
6 | export type NearByPoiType = 'attraction' | 'restaurant'
7 | export type ListingPoi = ListingAttraction | ListingRestaurant
8 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/actions/index.ts:
--------------------------------------------------------------------------------
1 | export * from './actions'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/constants.ts:
--------------------------------------------------------------------------------
1 | export const HASH_COPY_ACTION_SHEET =
2 | 'poi-detail.detail-header.copy-action-sheet'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/index.ts:
--------------------------------------------------------------------------------
1 | export * from './actions'
2 | export * from './detail-header'
3 | export * from './detail-header-v2'
4 | export * from './image-carousel'
5 | export * from './images-provider'
6 | export * from './recommended-articles'
7 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/mocks/inventory-item.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [
3 | {
4 | "bucket": null,
5 | "desc": "asdf",
6 | "detailedDesc": "",
7 | "id": "4dbb5490-8d0e-406d-b9b3-8a897015e5a8",
8 | "image": "https://media.triple.guide/triple-cms/f_auto/0aec542e-8e97-4251-9b8b-6ba5c994ccdd.jpg",
9 | "target": "asdf",
10 | "text": null
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/recommended-articles/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './recommended-articles'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/recommended-articles/more-button.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 | import { Button } from '@titicaca/tds-ui'
3 |
4 | export const MoreButton = styled(Button)`
5 | width: 100%;
6 | text-align: center;
7 | padding: 8px 0;
8 | margin-top: 20px;
9 | font-size: 14px;
10 | font-weight: bold;
11 | `
12 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/recommended-articles/types.ts:
--------------------------------------------------------------------------------
1 | import { ImageMeta } from '@titicaca/type-definitions'
2 |
3 | export interface ArticleListingData {
4 | id: string
5 | type: 'article'
6 | source: {
7 | title: string
8 | image?: ImageMeta
9 | }
10 | reviewed: boolean
11 | scraped: boolean
12 | }
13 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-detail/types.ts:
--------------------------------------------------------------------------------
1 | export type ImageCategoryOrder =
2 | | 'image' // 대표 이미지
3 | | 'recommendation'
4 | | 'menuItem' // 대표 메뉴 이미지
5 | | 'menuBoard' // 메뉴판 이미지
6 | | 'featuredContent'
7 | | 'images' // 어드민에 등록된 이미지들 가운데 대표 이미지를 제외한 나머지 이미지
8 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-list-elements/constants.ts:
--------------------------------------------------------------------------------
1 | import { PoiListElementType } from './types'
2 |
3 | export const POI_IMAGE_PLACEHOLDERS: {
4 | [key in PoiListElementType['type']]: string
5 | } = {
6 | attraction: 'https://assets.triple.guide/images/ico_blank_see@2x.png',
7 | restaurant: 'https://assets.triple.guide/images/ico_blank_eat@2x.png',
8 | hotel: 'https://assets.triple.guide/images/ico_blank_hotel@2x.png',
9 | }
10 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-list-elements/get-type-names.ts:
--------------------------------------------------------------------------------
1 | import { PoiListElementType } from './types'
2 |
3 | export function getTypeNames(type: PoiListElementType['type']) {
4 | switch (type) {
5 | case 'attraction': {
6 | return '관광명소'
7 | }
8 | case 'hotel': {
9 | return '호텔'
10 | }
11 | case 'restaurant': {
12 | return '음식점'
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-list-elements/index.ts:
--------------------------------------------------------------------------------
1 | export * from './compact-poi-list-element'
2 | export * from './extended-poi-list-element'
3 | export * from './poi-list-element'
4 | export * from './poi-card-element'
5 | export * from './carousel-element'
6 | export * from './types'
7 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/poi-list-elements/poi-card-element/index.ts:
--------------------------------------------------------------------------------
1 | export * from './poi-card-element'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/pricing/index.ts:
--------------------------------------------------------------------------------
1 | export * from './fixed-pricing-v2'
2 | export * from './pricing'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/public-header/extra-action-item.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 |
3 | import { MIN_DESKTOP_WIDTH } from './constants'
4 |
5 | export const ExtraActionItem = styled.a`
6 | text-decoration: none;
7 | display: inline-flex;
8 | align-items: center;
9 | color: var(--color-gray800);
10 | font-size: 14px;
11 | padding: 10px 8px;
12 |
13 | @media (min-width: ${MIN_DESKTOP_WIDTH}px) {
14 | font-size: 17px;
15 | padding: 10px 14px;
16 | }
17 | `
18 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/public-header/extra-action-separator.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 |
3 | import { MIN_DESKTOP_WIDTH } from './constants'
4 |
5 | export const ExtraActionSeparator = styled.div`
6 | display: inline-block;
7 | width: 1px;
8 | margin: 0 2px;
9 | height: 10px;
10 | background-color: var(--color-gray100);
11 |
12 | @media (min-width: ${MIN_DESKTOP_WIDTH}px) {
13 | height: 14px;
14 | }
15 | `
16 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/public-header/extra-actions-container.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 |
3 | export const ExtraActionsContainer = styled.div`
4 | display: flex;
5 | align-items: center;
6 | margin-left: auto;
7 | `
8 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/public-header/index.ts:
--------------------------------------------------------------------------------
1 | export * from './public-header'
2 | export * from './extra-action-item'
3 | export * from './extra-action-separator'
4 | export * from './constants'
5 | export * from './side-menu'
6 | export * from './header-menu-button'
7 | export * from './side-menu/type'
8 |
9 | export { PublicHeader as default } from './public-header'
10 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/public-header/side-menu/type.ts:
--------------------------------------------------------------------------------
1 | interface MenuItemBase {
2 | label: string
3 | onClick?: () => void
4 | eventAction?: string
5 | tooltipDescription?: string
6 | }
7 |
8 | export type LinkMenuItem = MenuItemBase & {
9 | type: 'link'
10 | href: string
11 | }
12 |
13 | export type AccordionMenuItem = MenuItemBase & {
14 | type: 'accordion'
15 | subItems: LinkMenuItem[]
16 | defaultOpen?: boolean
17 | }
18 |
19 | export type MenuItem = LinkMenuItem | AccordionMenuItem
20 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/public-header/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react'
2 |
3 | export type Category = 'air' | 'hotels' | 'tna'
4 | export type DeeplinkComponent = ({
5 | deeplinkHref,
6 | }: {
7 | deeplinkHref: string
8 | }) => ReactElement
9 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/recommended-contents/index.ts:
--------------------------------------------------------------------------------
1 | export * from './recommended-contents'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/recommended-contents/recommended-contents.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import mock from './mocks/recommended-contents.sample.json'
4 | import { RecommendedContents } from './recommended-contents'
5 |
6 | export default {
7 | title: 'tds-widget / recommended-contents / RecommendedContents',
8 | component: RecommendedContents,
9 | } as Meta
10 |
11 | export const Basic: StoryObj = {
12 | args: {
13 | contents: mock.contents,
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/replies/hook.tsx:
--------------------------------------------------------------------------------
1 | import { useLoginCtaModal } from '@titicaca/triple-web'
2 |
3 | import { SessionError } from './replies-api-client'
4 |
5 | export function useHttpResponseError() {
6 | const { show: showLoginCtaModal } = useLoginCtaModal()
7 |
8 | return (error: SessionError | Error) => {
9 | if (error instanceof SessionError) {
10 | showLoginCtaModal()
11 | } else {
12 | // Handle other types of errors
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/replies/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './replies'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/replies/utils.ts:
--------------------------------------------------------------------------------
1 | import { Reply } from './types'
2 |
3 | export function checkUniqueReply(reply: Reply[]): Reply[] {
4 | const result = Array.from(
5 | new Map((reply || []).map((item) => [item.id, item])).values(),
6 | ).sort(
7 | (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
8 | )
9 |
10 | return result
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/resource-list-elements/index.ts:
--------------------------------------------------------------------------------
1 | export * from './extended-resource-list-element'
2 | export * from './resource-list-element-stats'
3 | export * from './review-scrap-stat'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/resource-list-elements/resource-list-element-stats.tsx:
--------------------------------------------------------------------------------
1 | import { Text } from '@titicaca/tds-ui'
2 |
3 | export function ResourceListElementStats({
4 | stats,
5 | ...textProps
6 | }: {
7 | stats: (string | null | undefined)[]
8 | } & Parameters[0]) {
9 | if (stats.length === 0) {
10 | return null
11 | }
12 |
13 | return {stats.filter((stat) => stat).join(' · ')}
14 | }
15 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/infinite-list/index.ts:
--------------------------------------------------------------------------------
1 | export * from './popular-reviews-infinite'
2 | export * from './latest-reviews-infinite'
3 | export * from './rating-infinite-list'
4 | export * from './types'
5 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/infinite-list/types.ts:
--------------------------------------------------------------------------------
1 | export interface InfiniteReviewProps {
2 | resourceId: string
3 | resourceType: string
4 | regionId?: string
5 | placeholderText?: string
6 | reviewsCount?: number
7 | recentTrip: boolean
8 | hasMedia: boolean
9 | }
10 |
11 | export type ExtendInfiniteReviewProps = InfiniteReviewProps & {
12 | sortingLabel: 'star-rating-asc' | 'star-rating-desc'
13 | }
14 |
15 | export type InfinityReviewValue =
16 | | InfiniteReviewProps
17 | | ExtendInfiniteReviewProps
18 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/review-element/comment.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 | import { Text } from '@titicaca/tds-ui'
3 |
4 | export function Comment({ children }: PropsWithChildren) {
5 | return (
6 |
7 | {children}
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/review-element/media/compare-media.ts:
--------------------------------------------------------------------------------
1 | import { ImageMeta } from '@titicaca/type-definitions'
2 |
3 | export function compareMedia(a: ImageMeta, b: ImageMeta) {
4 | if (a.type === b.type) {
5 | return 0
6 | }
7 |
8 | if (a.type === 'video') {
9 | return -1
10 | }
11 |
12 | return 1
13 | }
14 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/review-element/media/medium.tsx:
--------------------------------------------------------------------------------
1 | import { ImageMeta } from '@titicaca/type-definitions'
2 |
3 | import { Image } from './image'
4 | import { Video } from './video'
5 |
6 | interface Props {
7 | medium: ImageMeta
8 | }
9 |
10 | export function Medium({ medium }: Props) {
11 | const isVideo = medium.type === 'video'
12 |
13 | if (isVideo) {
14 | return
15 | }
16 |
17 | return
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/shorten-list/index.ts:
--------------------------------------------------------------------------------
1 | export * from './popular-reviews'
2 | export * from './latest-reviews'
3 | export * from './rating-reviews'
4 | export * from './types'
5 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/shorten-list/types.ts:
--------------------------------------------------------------------------------
1 | export interface ShortenReview {
2 | resourceId: string
3 | resourceType: string
4 | regionId?: string
5 | placeholderText?: string
6 | reviewsCount?: number
7 | recentTrip: boolean
8 | hasMedia: boolean
9 | }
10 |
11 | export type ExtendShortenReview = ShortenReview & {
12 | sortingLabel: 'star-rating-asc' | 'star-rating-desc'
13 | }
14 |
15 | export type ShortenReviewValue = ShortenReview | ExtendShortenReview
16 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/components/types.tsx:
--------------------------------------------------------------------------------
1 | export interface AppNativeActionProps {
2 | subscribeReviewUpdateEvent?: (
3 | handler: (params?: { id: string }) => void,
4 | ) => void
5 | unsubscribeReviewUpdateEvent?: (
6 | handler: (params?: { id: string }) => void,
7 | ) => void
8 | showToast: (message: string) => void
9 | notifyReviewDeleted: (resourceId: string, reviewId: string) => void
10 | }
11 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/constants.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_REVIEWS_COUNT_PER_PAGE = 20
2 | export const SHORTENED_REVIEWS_COUNT_PER_PAGE = 4
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/data/graphql/index.ts:
--------------------------------------------------------------------------------
1 | export * from './client'
2 | export * from './generated'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/data/graphql/mutation.graphql:
--------------------------------------------------------------------------------
1 | mutation LikeReview($reviewId: String!) {
2 | likeReview(reviewId: $reviewId) {
3 | id
4 | }
5 | }
6 |
7 | mutation UnlikeReview($reviewId: String!) {
8 | unlikeReview(reviewId: $reviewId)
9 | }
10 |
11 | mutation DeleteReview($id: ID!) {
12 | deleteReview(id: $id)
13 | }
14 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components/reviews-shorten'
2 | export * from './components/reviews'
3 | export * from './utils'
4 | export * from './services'
5 | export type { SortingOption, SortingType } from './components/sorting-context'
6 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/review/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-client-actions'
2 | export * from './use-reviews'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/scrap-button/index.ts:
--------------------------------------------------------------------------------
1 | export { ComposedOutlineScrapButton as OutlineScrapButton } from './outline-scrap-button'
2 | export { ComposedOverlayScrapButton as OverlayScrapButton } from './overlay-scrap-button'
3 | export { ScrapButtonMask } from './scrap-button-mask'
4 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/scrap-button/types.ts:
--------------------------------------------------------------------------------
1 | import type { TrackEventParams } from '@titicaca/triple-web'
2 |
3 | export interface ScrapableResource {
4 | id: string
5 | type: string
6 | scraped?: boolean
7 | }
8 |
9 | export interface ScrapIconProps {
10 | pressed: boolean
11 | size: number
12 | }
13 |
14 | export interface ScrapButtonProps {
15 | resource: ScrapableResource
16 | size?: number
17 | eventParams?: TrackEventParams
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/scrap-button/utils.ts:
--------------------------------------------------------------------------------
1 | import { MouseEventHandler } from 'react'
2 |
3 | /**
4 | * 주어진 핸들러에 propagation을 막는 로직을 추가한 핸들러를 반환합니다.
5 | * TODO: 비슷한 역할을 하는 함수들 통합
6 | * @param handler
7 | */
8 | export function createIsolatedClickHandler(
9 | handler: MouseEventHandler,
10 | ): MouseEventHandler {
11 | return (e) => {
12 | e.stopPropagation()
13 | e.preventDefault()
14 |
15 | handler(e)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/scrap/constants.ts:
--------------------------------------------------------------------------------
1 | export const START_SCRAPE = 'START_SCRAPE'
2 | export const SCRAPE = 'SCRAPE'
3 | export const SCRAPE_FAILED = 'SCRAPE_FAILED'
4 | export const START_UNSCRAPE = 'START_UNSCRAPE'
5 | export const UNSCRAPE = 'UNSCRAPE'
6 | export const UNSCRAPE_FAILED = 'UNSCRAPE_FAILED'
7 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/scrap/index.ts:
--------------------------------------------------------------------------------
1 | export * from './provider'
2 | export * from './use-scrap'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/scrap/types.ts:
--------------------------------------------------------------------------------
1 | import type { TrackEventParams } from '@titicaca/triple-web'
2 |
3 | export interface Scraps {
4 | [key: string]: boolean
5 | }
6 |
7 | export interface Target {
8 | id: string
9 | type: unknown
10 | eventParams?: TrackEventParams
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/search/index.ts:
--------------------------------------------------------------------------------
1 | export * from './search'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/social-reviews/index.ts:
--------------------------------------------------------------------------------
1 | export * from './external-links'
2 | export * from './social-review'
3 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/static-map/index.ts:
--------------------------------------------------------------------------------
1 | export * from './static-map'
2 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/static-map/static-map.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { StaticMap } from './static-map'
4 |
5 | export default {
6 | title: 'tds-widget / static-map / StaticMap',
7 | component: StaticMap,
8 | } as Meta
9 |
10 | export const Basic: StoryObj = {
11 | args: {
12 | type: 'attraction',
13 | lat: 35.6328964,
14 | lon: 139.8803943,
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/user-verification/index.ts:
--------------------------------------------------------------------------------
1 | export * from './confirmation-services'
2 | export * from './use-user-verification'
3 | export * from './verification-request'
4 |
5 | export { useSendVerifiedMessage } from './verified-message'
6 | export type { VerifiedMessage } from './verified-message'
7 | export type { VerificationType } from './types'
8 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/user-verification/types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @deprecated 다양한 외부 프로모션 타입에 대응하기 위해 string 타입을 사용합니다.
3 | */
4 | export type VerificationType = string
5 |
--------------------------------------------------------------------------------
/packages/tds-widget/src/user-verification/verification-request.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { VerificationRequest } from './verification-request'
4 |
5 | export default {
6 | title: 'tds-widget / user-verification / VerificationRequest',
7 | component: VerificationRequest,
8 | } as Meta
9 |
10 | // TODO: 서버에 데이터가 없어서 mocking 해야 할 듯
11 | export const ExampleVerificationRequest: StoryObj =
12 | {
13 | args: {
14 | forceVerification: false,
15 | onCancel: () => {},
16 | },
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-widget/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tds-widget/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tds-widget/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/anchor.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 |
3 | const Element = styled.div`
4 | height: 0;
5 | `
6 |
7 | export default function Anchor({
8 | value: { href },
9 | }: {
10 | value: { href: string }
11 | }) {
12 | return
13 | }
14 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/animation.tsx:
--------------------------------------------------------------------------------
1 | import { TripleHeader, TripleHeaderProps } from '@titicaca/triple-header'
2 |
3 | export default function Animation({ value }: { value: TripleHeaderProps }) {
4 | return {value}
5 | }
6 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/coupon/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @reference https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color
3 | */
4 | export function safeParseHexColor(color?: string) {
5 | if (!color) {
6 | return
7 | }
8 |
9 | const HEX_ALPHA_PATTERN = /^#([A-F0-9]{3,4}|[A-F0-9]{6}|[A-F0-9]{8})$/i
10 | const colorCode = color.startsWith('#') ? color : `#${color}`
11 |
12 | return HEX_ALPHA_PATTERN.test(colorCode) ? colorCode : color
13 | }
14 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/itinerary/types.ts:
--------------------------------------------------------------------------------
1 | import { PoiItineraryItemType } from '@titicaca/content-type-definitions'
2 |
3 | export type ItineraryElementType = PoiItineraryItemType['poi']['type'] | 'festa'
4 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/shared/generate-click-handler.ts:
--------------------------------------------------------------------------------
1 | import { LinkEventHandler, ImageEventHandler } from '../../types'
2 |
3 | export default function generateClickHandler(
4 | onLinkClick?: LinkEventHandler,
5 | onImageClick?: ImageEventHandler,
6 | ): ImageEventHandler {
7 | return (e, image) => {
8 | if (image.link && image.link.href && onLinkClick) {
9 | return onLinkClick(e, image.link)
10 | } else if (onImageClick) {
11 | return onImageClick(e, image)
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/shared/resource-list.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 | import { List } from '@titicaca/tds-ui'
3 |
4 | export default function ResourceList({ children }: PropsWithChildren) {
5 | return {children}
6 | }
7 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/table.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Container,
3 | ContainerProps,
4 | Table as TableView,
5 | TableProps,
6 | } from '@titicaca/tds-ui'
7 |
8 | export default function Table({
9 | value: { table },
10 | ...props
11 | }: {
12 | value: { table: TableProps }
13 | } & ContainerProps) {
14 | return (
15 |
21 |
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/packages/triple-document/src/elements/text/index.ts:
--------------------------------------------------------------------------------
1 | export * from './headings'
2 | export { default as Text } from './plain'
3 |
--------------------------------------------------------------------------------
/packages/triple-document/src/prop-context/deep-link.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 |
3 | const DeepLinkContext = createContext(undefined)
4 |
5 | export const DeepLinkProvider = DeepLinkContext.Provider
6 |
7 | export function useDeepLink() {
8 | return useContext(DeepLinkContext)
9 | }
10 |
--------------------------------------------------------------------------------
/packages/triple-document/src/prop-context/guest-mode.ts:
--------------------------------------------------------------------------------
1 | import { GuestModeType } from '@titicaca/type-definitions'
2 | import { createContext, useContext } from 'react'
3 |
4 | const GuestModeContext = createContext(undefined)
5 |
6 | export const GuestModeProvider = GuestModeContext.Provider
7 |
8 | export function useGuestMode() {
9 | return useContext(GuestModeContext)
10 | }
11 |
--------------------------------------------------------------------------------
/packages/triple-document/src/prop-context/image-click-handler.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 |
3 | import { ImageEventHandler } from '../types'
4 |
5 | const ImageClickHandlerContext = createContext(
6 | undefined,
7 | )
8 |
9 | export const ImageClickHandlerProvider = ImageClickHandlerContext.Provider
10 |
11 | export function useImageClickHandler() {
12 | return useContext(ImageClickHandlerContext)
13 | }
14 |
--------------------------------------------------------------------------------
/packages/triple-document/src/prop-context/image-source.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 | import { ImageSource } from '@titicaca/tds-widget'
3 |
4 | const ImageSourceContext = createContext(
5 | undefined,
6 | )
7 |
8 | export const ImageSourceProvider = ImageSourceContext.Provider
9 |
10 | export function useImageSource() {
11 | return useContext(ImageSourceContext)
12 | }
13 |
--------------------------------------------------------------------------------
/packages/triple-document/src/prop-context/link-click-handler.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 |
3 | import { LinkEventHandler } from '../types'
4 |
5 | const LinkClickHandlerContext = createContext(
6 | undefined,
7 | )
8 |
9 | export const LinkClickHandlerProvider = LinkClickHandlerContext.Provider
10 |
11 | export function useLinkClickHandler() {
12 | return useContext(LinkClickHandlerContext)
13 | }
14 |
--------------------------------------------------------------------------------
/packages/triple-document/src/prop-context/resource-click-handler.ts:
--------------------------------------------------------------------------------
1 | import { createContext, SyntheticEvent, useContext } from 'react'
2 |
3 | export type ResourceClickHandler = (
4 | e: SyntheticEvent,
5 | resource: {
6 | id: string
7 | type: string
8 | source: unknown
9 | },
10 | ) => void
11 |
12 | const ResourceClickHandlerContext = createContext<
13 | ResourceClickHandler | undefined
14 | >(undefined)
15 | export const ResourceClickHandlerProvider = ResourceClickHandlerContext.Provider
16 |
17 | export function useResourceClickHandler() {
18 | return useContext(ResourceClickHandlerContext)
19 | }
20 |
--------------------------------------------------------------------------------
/packages/triple-document/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-document/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-document/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-email-document/src/common/box.ts:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 | import { MarginPadding } from '@titicaca/tds-ui'
3 |
4 | const Box = styled.td<{ $padding?: MarginPadding }>`
5 | /* stylelint-disable function-whitespace-after */
6 | padding: ${({ $padding }) => {
7 | const { top, bottom, left, right } = $padding || {}
8 | return [top, right, bottom, left].map(addUnit).join(' ')
9 | }};
10 | `
11 |
12 | function addUnit(value: string | number | undefined) {
13 | return value !== undefined && value !== 0 ? `${value}px` : 0
14 | }
15 |
16 | export default Box
17 |
--------------------------------------------------------------------------------
/packages/triple-email-document/src/common/handlebars-anchor.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 |
3 | export default function HandlebarsAnchor({
4 | linkId,
5 | className,
6 | children,
7 | }: PropsWithChildren<{
8 | linkId: string
9 | className?: string
10 | }>) {
11 | return (
12 |
17 | {children}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/triple-email-document/src/common/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Box } from './box'
2 | export { default as FluidTable } from './fluid-table'
3 | export { default as HandlebarsAnchor } from './handlebars-anchor'
4 | export {
5 | RecommendedReset,
6 | CustomReset,
7 | ClientSpecificWorkaround,
8 | } from './reset'
9 |
--------------------------------------------------------------------------------
/packages/triple-email-document/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export { default as EmailPreview, type PreviewDocument } from './preview'
2 |
--------------------------------------------------------------------------------
/packages/triple-email-document/src/components/preview.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 |
3 | import EmailPreview from './preview'
4 |
5 | test('EmailPreview를 렌더링합니다.', () => {
6 | const mockedPreviewValue = {
7 | phrase: 'Cheking Email',
8 | }
9 |
10 | render()
11 |
12 | const previewElement = screen.getByText(mockedPreviewValue.phrase)
13 |
14 | expect(previewElement).toBeInTheDocument()
15 | })
16 |
--------------------------------------------------------------------------------
/packages/triple-email-document/src/index.ts:
--------------------------------------------------------------------------------
1 | import { TripleEmailDocument } from './triple-email-document'
2 |
3 | export { default as ELEMENTS } from './elements'
4 | export type { TripleEmailElementData as TripleEmailDocumentElement } from './elements'
5 | export type { ExtendedImageMeta, ImageDocument } from './elements/images'
6 | export { EmailPreview, type PreviewDocument } from './components'
7 | export { default as FullEmailTemplate } from './full-email-template'
8 | export { FluidTable, HandlebarsAnchor } from './common'
9 |
10 | export default TripleEmailDocument
11 |
--------------------------------------------------------------------------------
/packages/triple-email-document/src/preview.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 |
3 | import { EmailPreview } from './components'
4 |
5 | export default {
6 | title: 'triple-email-document / Preview',
7 | component: EmailPreview,
8 | argTypes: {
9 | value: {
10 | phrase: {
11 | type: 'string',
12 | require: true,
13 | },
14 | },
15 | },
16 | } as Meta
17 |
18 | export const DefaultPreview: StoryObj = {
19 | name: '미리보기',
20 | args: {
21 | value: {
22 | phrase: '지금 10만원 받고, 트리플로 새해여행 어때요?',
23 | },
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/packages/triple-email-document/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-email-document/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-email-document/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-fallback-action/src/constant.ts:
--------------------------------------------------------------------------------
1 | export const TRIPLE_FALLBACK_ACTION_CLASS_NAME = '-triple-fallback-action'
2 |
--------------------------------------------------------------------------------
/packages/triple-fallback-action/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './constant'
2 | export * from './triple-fallback-action-remover'
3 | export * from './triple-fallback-action'
4 | export * from './use-triple-fallback-action-remover'
5 |
--------------------------------------------------------------------------------
/packages/triple-fallback-action/src/triple-fallback-action-remover.tsx:
--------------------------------------------------------------------------------
1 | import { useTripleFallbackActionRemover } from './use-triple-fallback-action-remover'
2 |
3 | export function TripleFallbackActionRemover() {
4 | useTripleFallbackActionRemover()
5 |
6 | return null
7 | }
8 |
--------------------------------------------------------------------------------
/packages/triple-fallback-action/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-fallback-action/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-fallback-action/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-header/src/frame/common.ts:
--------------------------------------------------------------------------------
1 | import { SyntheticEvent } from 'react'
2 |
3 | import { Link, LinkEventHandler } from '../types'
4 |
5 | export function generateLinkClickHandler(
6 | onLinkClick?: LinkEventHandler,
7 | ): (e: SyntheticEvent, link?: Link) => void {
8 | return (e, link) => {
9 | if (link && onLinkClick) {
10 | return onLinkClick(e, link)
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/triple-header/src/frame/effects/index.ts:
--------------------------------------------------------------------------------
1 | import { Zoom, ZoomEffect } from './zoom'
2 | import { Rotate, RotateEffect } from './rotate'
3 | import { Flying, FlyingEffect } from './flying'
4 |
5 | export const EFFECTS = {
6 | zoom: Zoom,
7 | rotate: Rotate,
8 | flying: Flying,
9 | }
10 |
11 | export type Effect = ZoomEffect | RotateEffect | FlyingEffect
12 |
--------------------------------------------------------------------------------
/packages/triple-header/src/frame/effects/types.ts:
--------------------------------------------------------------------------------
1 | type RepeatType = 'loop' | 'reverse' | 'mirror'
2 |
3 | export interface InitialEffectOptions {
4 | infinity?: boolean
5 | repeatType?: RepeatType
6 | }
7 |
--------------------------------------------------------------------------------
/packages/triple-header/src/frame/index.ts:
--------------------------------------------------------------------------------
1 | import { ImageFrame } from './image'
2 | import { TextFrame } from './text'
3 |
4 | export const FRAMES = {
5 | image: ImageFrame,
6 | text: TextFrame,
7 | }
8 |
9 | export { Frame } from './frame'
10 |
--------------------------------------------------------------------------------
/packages/triple-header/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './triple-header'
2 | export type { TripleHeaderProps } from './types'
3 |
--------------------------------------------------------------------------------
/packages/triple-header/src/layer/index.ts:
--------------------------------------------------------------------------------
1 | export { Layer } from './layer'
2 |
--------------------------------------------------------------------------------
/packages/triple-header/src/layer/transitions/index.ts:
--------------------------------------------------------------------------------
1 | import { Slide } from './slide'
2 | import { FadeInOut } from './fade-in-out'
3 | import { Rolling } from './rolling'
4 | import { Marquee } from './marquee'
5 |
6 | export const TRANSITIONS = {
7 | slide: Slide,
8 | fadeInOut: FadeInOut,
9 | rolling: Rolling,
10 | marquee: Marquee,
11 | }
12 |
--------------------------------------------------------------------------------
/packages/triple-header/src/lottie/index.ts:
--------------------------------------------------------------------------------
1 | export { Lottie } from './lottie'
2 |
--------------------------------------------------------------------------------
/packages/triple-header/src/motion-container.ts:
--------------------------------------------------------------------------------
1 | import { styled } from 'styled-components'
2 | import { motion } from 'framer-motion'
3 |
4 | export const MotionContainer = styled(motion.div)`
5 | position: absolute;
6 | top: 0;
7 | width: 100%;
8 | height: 100%;
9 | `
10 |
--------------------------------------------------------------------------------
/packages/triple-header/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-header/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-header/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/helpers/session.ts:
--------------------------------------------------------------------------------
1 | import type { IncomingMessage } from 'http'
2 |
3 | import Cookies from 'universal-cookie'
4 | import { SESSION_KEY, TP_TK } from '@titicaca/constants'
5 |
6 | export function checkSession(req: IncomingMessage) {
7 | const cookies = new Cookies(req.headers.cookie)
8 |
9 | return !!cookies.get(TP_TK) || !!cookies.get(SESSION_KEY)
10 | }
11 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/helpers/user-agent.ts:
--------------------------------------------------------------------------------
1 | import type { UserAgentValue } from '@titicaca/triple-web'
2 | import { isMobile } from '@titicaca/triple-web-utils'
3 | import { UAParser } from 'ua-parser-js'
4 |
5 | interface Params {
6 | userAgent: string | undefined
7 | }
8 |
9 | export function extractUserAgent({ userAgent }: Params): UserAgentValue {
10 | const parser = new UAParser(userAgent ?? undefined)
11 | const result = parser.getResult()
12 |
13 | return {
14 | ...result,
15 | isMobile: isMobile(result),
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './initializers'
2 | export * from './providers'
3 | export * from './ssr-utils'
4 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/initializers/client-app.ts:
--------------------------------------------------------------------------------
1 | import type { NextPageContext } from 'next'
2 | import { type ClientAppValue } from '@titicaca/triple-web'
3 |
4 | import { extractClientApp } from '../helpers/client-app'
5 |
6 | export function getClientApp(ctx: NextPageContext): ClientAppValue {
7 | const userAgent = ctx.req
8 | ? ctx.req.headers['user-agent']
9 | : window.navigator.userAgent
10 |
11 | const autoplay = ctx.req?.headers['x-triple-autoplay']
12 | const networkType = ctx.req?.headers['x-triple-network-type']
13 |
14 | return extractClientApp({ autoplay, networkType, userAgent })
15 | }
16 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/initializers/user-agent.ts:
--------------------------------------------------------------------------------
1 | import type { NextPageContext } from 'next'
2 | import type { UserAgentValue } from '@titicaca/triple-web'
3 |
4 | import { extractUserAgent } from '../helpers/user-agent'
5 |
6 | export function getUserAgent(ctx: NextPageContext): UserAgentValue {
7 | const userAgent = ctx.req
8 | ? ctx.req.headers['user-agent']
9 | : window.navigator.userAgent
10 |
11 | return extractUserAgent({ userAgent })
12 | }
13 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/providers/app-install-cta-modal-provider.tsx:
--------------------------------------------------------------------------------
1 | export {
2 | AppInstallCtaModalProvider,
3 | type AppInstallCtaModalProviderProps,
4 | } from '@titicaca/triple-web'
5 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/providers/event-metadata-provider.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # EventMetadatProvider
6 |
7 | ## Usage
8 |
9 | ```tsx
10 | export function Page({ children }) {
11 | return (
12 |
17 | {children}
18 |
19 | )
20 | }
21 | ```
22 |
23 | ## Props
24 |
25 | ### children
26 |
27 | `ReactNode`
28 |
29 | ### eventMetadataContext
30 |
31 | `EventMetadataValue`
32 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/providers/event-metadata-provider.tsx:
--------------------------------------------------------------------------------
1 | export {
2 | EventMetadataProvider,
3 | type EventMetadataProviderProps,
4 | } from '@titicaca/triple-web'
5 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/providers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app-install-cta-modal-provider'
2 | export * from './event-metadata-provider'
3 | export * from './event-tracking-provider'
4 | export * from './login-cta-modal-provider'
5 | export * from './triple-web'
6 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/providers/login-cta-modal-provider.tsx:
--------------------------------------------------------------------------------
1 | export {
2 | LoginCtaModalProvider,
3 | type LoginCtaModalProviderProps,
4 | } from '@titicaca/triple-web'
5 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/ssr-utils/get-client-app.ts:
--------------------------------------------------------------------------------
1 | import type { GetServerSidePropsContext } from 'next'
2 | import type { ClientAppValue } from '@titicaca/triple-web'
3 |
4 | import { extractClientApp } from '../helpers/client-app'
5 |
6 | export function getClientApp(ctx: GetServerSidePropsContext): ClientAppValue {
7 | const userAgent = ctx.req.headers['user-agent']
8 |
9 | const autoplay = ctx.req?.headers['x-triple-autoplay']
10 | const networkType = ctx.req?.headers['x-triple-network-type']
11 |
12 | return extractClientApp({ autoplay, networkType, userAgent })
13 | }
14 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/ssr-utils/get-session-availability.ts:
--------------------------------------------------------------------------------
1 | import type { GetServerSidePropsContext } from 'next'
2 |
3 | import { checkSession } from '../helpers/session'
4 |
5 | export function getSessionAvailability(
6 | ctx: GetServerSidePropsContext,
7 | ): boolean {
8 | return checkSession(ctx.req)
9 | }
10 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/ssr-utils/get-user-agent.ts:
--------------------------------------------------------------------------------
1 | import type { GetServerSidePropsContext } from 'next'
2 | import type { UserAgentValue } from '@titicaca/triple-web'
3 |
4 | import { extractUserAgent } from '../helpers/user-agent'
5 |
6 | export function getUserAgent(ctx: GetServerSidePropsContext): UserAgentValue {
7 | const userAgent = ctx.req
8 | ? (ctx.req.headers['user-agent'] ?? '')
9 | : window.navigator.userAgent
10 |
11 | return extractUserAgent({ userAgent })
12 | }
13 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/src/ssr-utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth-guard'
2 | export * from './get-client-app'
3 | export * from './get-session-availability'
4 | export * from './get-user-agent'
5 | export * from './put-invalid-session-id-remover'
6 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs-pages/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './initializers'
2 | export * from './providers'
3 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/initializers/index.ts:
--------------------------------------------------------------------------------
1 | import type { TripleWebProps } from '../providers'
2 |
3 | import { getClientApp } from './client-app'
4 | import { getSession } from './session'
5 | import { getUserAgent } from './user-agent'
6 |
7 | export type BuildTripleWebPropsResult = Omit<
8 | TripleWebProps,
9 | 'children' | 'envProvider' | 'i18nProvider'
10 | >
11 |
12 | export async function buildTripleWebProps(): Promise {
13 | return {
14 | clientAppProvider: getClientApp(),
15 | sessionProvider: await getSession(),
16 | userAgentProvider: getUserAgent(),
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/initializers/user-agent.ts:
--------------------------------------------------------------------------------
1 | import 'server-only'
2 |
3 | import { headers } from 'next/headers'
4 | import UAParser from 'ua-parser-js'
5 | import type { UserAgentValue } from '@titicaca/triple-web'
6 | import { isMobile } from '@titicaca/triple-web-utils'
7 |
8 | export function getUserAgent(): UserAgentValue {
9 | const headersList = headers()
10 | const userAgent = headersList.get('user-agent')
11 | const parser = new UAParser(userAgent ?? undefined)
12 | const result = parser.getResult()
13 |
14 | return {
15 | ...result,
16 | isMobile: isMobile(result),
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/providers/app-install-cta-modal-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | export {
4 | AppInstallCtaModalProvider,
5 | type AppInstallCtaModalProviderProps,
6 | } from '@titicaca/triple-web'
7 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/providers/event-metadata-provider.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # EventMetadatProvider
6 |
7 | ## Usage
8 |
9 | ```tsx
10 | export function Page({ children }) {
11 | return (
12 |
17 | {children}
18 |
19 | )
20 | }
21 | ```
22 |
23 | ## Props
24 |
25 | ### children
26 |
27 | `ReactNode`
28 |
29 | ### eventMetadataContext
30 |
31 | `EventMetadataValue`
32 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/providers/event-metadata-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | export {
4 | EventMetadataProvider,
5 | type EventMetadataProviderProps,
6 | } from '@titicaca/triple-web'
7 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/providers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app-install-cta-modal-provider'
2 | export * from './event-metadata-provider'
3 | export * from './event-tracking-provider'
4 | export * from './login-cta-modal-provider'
5 | export * from './triple-web'
6 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/src/providers/login-cta-modal-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | export {
4 | LoginCtaModalProvider,
5 | type LoginCtaModalProviderProps,
6 | } from '@titicaca/triple-web'
7 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-web-nextjs/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-web-test-utils/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './create-test-wrapper'
2 |
--------------------------------------------------------------------------------
/packages/triple-web-test-utils/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web-test-utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-web-test-utils/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/src/check-client-app.ts:
--------------------------------------------------------------------------------
1 | import { clientAppRegex } from './regex'
2 |
3 | export function checkClientApp(userAgent: string) {
4 | return clientAppRegex.test(userAgent)
5 | }
6 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './check-client-app'
2 | export * from './regex'
3 | export * from './user'
4 | export * from './user-agent'
5 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/src/regex.ts:
--------------------------------------------------------------------------------
1 | export const clientAppRegex = /(Triple-iOS|Triple-Android)\/([^ ]+)/
2 |
3 | export const macAppRegex = /TripleMacApp/
4 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/src/user-agent.ts:
--------------------------------------------------------------------------------
1 | import type { IResult } from 'ua-parser-js'
2 |
3 | export function isMobile(userAgent: IResult) {
4 | const { device } = userAgent
5 |
6 | if (device.type === 'mobile' || device.type === 'tablet') {
7 | return true
8 | } else {
9 | return false
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/src/user.ts:
--------------------------------------------------------------------------------
1 | export const GET_USER_REQUEST_URL = '/api/users/me'
2 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-web-utils/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/triple-web/src/client-app/context.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 |
5 | import type { ClientAppValue } from './types'
6 |
7 | export const ClientAppContext = createContext(
8 | undefined,
9 | )
10 |
--------------------------------------------------------------------------------
/packages/triple-web/src/client-app/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './use-client-app-actions'
3 | export * from './use-client-app-callback'
4 | export * from './use-client-app'
5 | export * from './use-feature-flag'
6 |
--------------------------------------------------------------------------------
/packages/triple-web/src/client-app/types.ts:
--------------------------------------------------------------------------------
1 | export const enum ClientAppName {
2 | iOS = 'Triple-iOS',
3 | Android = 'Triple-Android',
4 | }
5 |
6 | export type ClientAppValue = {
7 | metadata: {
8 | name: ClientAppName
9 | version: string
10 | isMacApp: boolean
11 | }
12 | device: {
13 | autoplay: 'always' | 'wifi_only' | 'never'
14 | networkType: 'wifi' | 'cellular' | 'unknown'
15 | }
16 | } | null
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/client-app/use-client-app-actions.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useClientAppActions
6 |
7 | 클라이언트 앱과 통신하는 인터페이스를 사용합니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const clientAppActions = useClientAppActions()
13 | ```
14 |
15 | ## Returns
16 |
17 | WebToNativeInterfaces
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/client-app/use-client-app.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useClientApp
6 |
7 | ClientAppContext 값을 가져옵니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const clientApp = useClientApp()
13 | ```
14 |
15 | ## Returns
16 |
17 | `ClientAppContext`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/client-app/use-client-app.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { ClientAppContext } from './context'
4 |
5 | /**
6 | * ClientAppContext 값을 가져옵니다.
7 | */
8 | export function useClientApp() {
9 | const context = useContext(ClientAppContext)
10 |
11 | if (context === undefined) {
12 | throw new Error('ClientAppContext가 없습니다.')
13 | }
14 |
15 | return context
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/env/context.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 |
5 | import type { EnvValue } from './types'
6 |
7 | export const EnvContext = createContext(undefined)
8 |
--------------------------------------------------------------------------------
/packages/triple-web/src/env/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './use-env'
3 |
--------------------------------------------------------------------------------
/packages/triple-web/src/env/use-env.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useEnv
6 |
7 | EnvContext 값을 가져옵니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const env = useEnv()
13 | ```
14 |
15 | ## Returns
16 |
17 | `EnvContext`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/env/use-env.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { EnvContext } from './context'
4 |
5 | /**
6 | * EnvContext 값을 가져옵니다.
7 | */
8 | export function useEnv() {
9 | const context = useContext(EnvContext)
10 |
11 | if (context === undefined) {
12 | throw new Error('EnvContext가 없습니다.')
13 | }
14 |
15 | return context
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/context.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext, useContext } from 'react'
4 |
5 | import type { EventMetadataValue, EventTrackingValue } from './types'
6 |
7 | export const EventMetadataContext = createContext<
8 | EventMetadataValue | undefined
9 | >(undefined)
10 |
11 | export function useEventMetadata() {
12 | return useContext(EventMetadataContext)
13 | }
14 |
15 | export const EventTrackingContext = createContext<
16 | EventTrackingValue | undefined
17 | >(undefined)
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './use-set-firebase-user-id'
3 | export * from './use-track-event-with-metadata'
4 | export * from './use-track-event'
5 | export * from './use-track-screen'
6 | export * from './use-utm'
7 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/libs/firebase-analytics.ts:
--------------------------------------------------------------------------------
1 | import { getApp } from 'firebase/app'
2 | import { getAnalytics } from 'firebase/analytics'
3 |
4 | export function getFirebaseAnalytics() {
5 | try {
6 | const app = getApp()
7 | const analytics = getAnalytics(app)
8 |
9 | return analytics
10 | } catch {}
11 | }
12 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/types.ts:
--------------------------------------------------------------------------------
1 | export interface EventTrackingUtmValue {
2 | source?: string
3 | medium?: string
4 | campaign?: string
5 | term?: string
6 | content?: string
7 |
8 | partner?: string
9 | }
10 |
11 | export interface EventTrackingValue {
12 | page: {
13 | label: string
14 | path: string
15 | }
16 | utm: EventTrackingUtmValue
17 | onError?: (error: Error) => void
18 | }
19 |
20 | export interface EventMetadataValue {
21 | [key: string]: string
22 | }
23 |
24 | export type { TrackEventParams } from './utils/track-event'
25 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-set-firebase-user-id.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useSetFirebaseUserId
6 |
7 | ## Usage
8 |
9 | ```tsx
10 | const setFirebaseUserId = useSetFirebaseUserId()
11 | ```
12 |
13 | ## Returns
14 |
15 | `(userId: string | null) => void`
16 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-set-firebase-user-id.ts:
--------------------------------------------------------------------------------
1 | import { setUserId } from 'firebase/analytics'
2 | import { useCallback } from 'react'
3 |
4 | import { getFirebaseAnalytics } from './libs/firebase-analytics'
5 |
6 | /**
7 | * Firebase user ID 설정.
8 | */
9 | export function useSetFirebaseUserId() {
10 | return useCallback((userId: string | null) => {
11 | const firebaseAnalytics = getFirebaseAnalytics()
12 | if (firebaseAnalytics) {
13 | setUserId(firebaseAnalytics, userId || '')
14 | }
15 | }, [])
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-track-event-with-metadata.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useTrackEventWithMetadata
6 |
7 | ## Usage
8 |
9 | ```tsx
10 | const trackEvent = useTrackEventWithMetadata()
11 | ```
12 |
13 | ## Returns
14 |
15 | `(params: TrackEventParams) => void`
16 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-track-event.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # trackEvent
6 |
7 | ## Usage
8 |
9 | ```tsx
10 | const trackEvent = useTrackEvent()
11 | ```
12 |
13 | ## Returns
14 |
15 | `(params: TrackEventParams) => void`
16 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-track-event.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useContext } from 'react'
2 |
3 | import { EventTrackingContext } from './context'
4 | import { type TrackEventParams, trackEvent } from './utils/track-event'
5 |
6 | /**
7 | * 이벤트 트래킹을 사용합니다.
8 | */
9 | export function useTrackEvent() {
10 | const context = useContext(EventTrackingContext)
11 |
12 | return useCallback(
13 | (params: TrackEventParams) =>
14 | context ? trackEvent(params, context) : undefined,
15 | [context],
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-track-screen.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useTrackScreen
6 |
7 | ## Usage
8 |
9 | ```tsx
10 | const env = useTrackScreen()
11 | ```
12 |
13 | ## Returns
14 |
15 | `(path: string, label?: string, additionalMetadata?: { [key: string]: string }) => void`
16 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-triple-web-device-id.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import Cookies from 'universal-cookie'
3 | import { X_TRIPLE_WEB_DEVICE_ID } from '@titicaca/constants'
4 |
5 | export function useTripleWebDeviceId() {
6 | const [tripleWebDeviceId, setTripleWebDeviceId] = useState<
7 | string | undefined
8 | >()
9 |
10 | useEffect(() => {
11 | setTripleWebDeviceId(
12 | new Cookies(document.cookie).get(X_TRIPLE_WEB_DEVICE_ID),
13 | )
14 | }, [])
15 |
16 | return tripleWebDeviceId
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-utm.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useUtm
6 |
7 | ## Usage
8 |
9 | ```tsx
10 | const { source, medium. campaign, term, content, partner } = useUtm()
11 | ```
12 |
13 | ## Returns
14 |
15 | ### source
16 |
17 | `string`
18 |
19 | ### medium
20 |
21 | `string`
22 |
23 | ### campaign
24 |
25 | `string`
26 |
27 | ### term
28 |
29 | `string`
30 |
31 | ### content
32 |
33 | `string`
34 |
35 | ### partner
36 |
37 | `string`
38 |
--------------------------------------------------------------------------------
/packages/triple-web/src/event-tracking/use-utm.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { EventTrackingContext } from './context'
4 |
5 | /**
6 | * UTM 값을 가져옵니다.
7 | */
8 | export function useUtm() {
9 | const context = useContext(EventTrackingContext)
10 |
11 | if (context) {
12 | return context.utm
13 | }
14 |
15 | return {}
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/hash-router/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-hash-router'
2 |
--------------------------------------------------------------------------------
/packages/triple-web/src/hash-router/use-hash-router.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { HashRouterContext } from './context'
4 |
5 | /**
6 | * HashRouterContext 값을 가져옵니다.
7 | */
8 | export function useHashRouter() {
9 | const context = useContext(HashRouterContext)
10 |
11 | if (!context) {
12 | throw new Error('HashRouterContext가 존재하지 않습니다.')
13 | }
14 |
15 | return context
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/i18n/context.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 |
5 | import type { I18nValue } from './types'
6 |
7 | export const I18nContext = createContext(undefined)
8 |
--------------------------------------------------------------------------------
/packages/triple-web/src/i18n/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './use-i18n'
3 | export * from './use-translation'
4 |
--------------------------------------------------------------------------------
/packages/triple-web/src/i18n/types.ts:
--------------------------------------------------------------------------------
1 | export type I18nLocale = 'en' | 'ja' | 'ko' | 'zh-TW'
2 |
3 | export interface I18nValue {
4 | defaultLocale: I18nLocale
5 | locale?: I18nLocale
6 | }
7 |
--------------------------------------------------------------------------------
/packages/triple-web/src/i18n/use-i18n.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useI18n
6 |
7 | I18nValue 값을 가져옵니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const useI18n = useI18n()
13 | ```
14 |
15 | ## Returns
16 |
17 | `I18Context`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/i18n/use-i18n.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { I18nContext } from './context'
4 |
5 | /**
6 | * I18nContext 값을 가져옵니다.
7 | */
8 | export function useI18n() {
9 | const context = useContext(I18nContext)
10 |
11 | if (!context) {
12 | throw new Error('I18nContext가 없습니다.')
13 | }
14 |
15 | return context
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/i18n/use-translation.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useTranslation
6 |
7 | 번역 함수를 사용합니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const t = useTranslation()
13 | ```
14 |
15 | ## Returns
16 |
17 | `(key: string, values = {}) => string`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './client-app'
2 | export * from './env'
3 | export * from './event-tracking'
4 | export * from './hash-router'
5 | export * from './i18n'
6 | export * from './modal'
7 | export * from './providers'
8 | export * from './session'
9 | export * from './user-agent'
10 |
--------------------------------------------------------------------------------
/packages/triple-web/src/modal/app-install-cta-modal-context.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 |
5 | import type { AppInstallCtaModalRef } from './types'
6 |
7 | export interface AppInstallCtaModalContextValue {
8 | showOptions?: AppInstallCtaModalRef
9 | }
10 |
11 | export const AppInstallCtaModalContext = createContext<
12 | AppInstallCtaModalContextValue | undefined
13 | >(undefined)
14 |
--------------------------------------------------------------------------------
/packages/triple-web/src/modal/constants.ts:
--------------------------------------------------------------------------------
1 | export const LOGIN_CTA_MODAL_HASH = 'login-cta-modal'
2 | export const APP_INSTALL_CTA_MODAL_HASH = 'app-install-cta-modal'
3 |
--------------------------------------------------------------------------------
/packages/triple-web/src/modal/index.ts:
--------------------------------------------------------------------------------
1 | export * from './constants'
2 | export * from './use-login-cta-modal'
3 | export * from './use-app-install-cta-modal'
4 |
--------------------------------------------------------------------------------
/packages/triple-web/src/modal/login-cta-modal-context.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 |
5 | import type { LoginCtaModalRef } from './types'
6 |
7 | export interface LoginCtaModalContextValue {
8 | showOptions?: LoginCtaModalRef
9 | }
10 |
11 | export const LoginCtaModalContext = createContext<
12 | LoginCtaModalContextValue | undefined
13 | >(undefined)
14 |
--------------------------------------------------------------------------------
/packages/triple-web/src/modal/use-app-install-cta-modal.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useAppInstallCtaModal
6 |
7 | 앱 설치 유도 modal을 관리합니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const { show, close } = useAppInstallCtaModal()
13 | ```
14 |
15 | ## Returns
16 |
17 | ### show
18 |
19 | `(options?: ShowOptions) => void`
20 |
21 | ### close
22 |
23 | `() => void`
24 |
--------------------------------------------------------------------------------
/packages/triple-web/src/modal/use-login-cta-modal.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useLoginCtaModal
6 |
7 | 로그인 유도 modal을 관리합니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const { show, close } = useLoginCtaModal()
13 | ```
14 |
15 | ## Returns
16 |
17 | ### show
18 |
19 | `(options?: ShowOptions) => void`
20 |
21 | ### close
22 |
23 | `() => void`
24 |
--------------------------------------------------------------------------------
/packages/triple-web/src/providers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app-install-cta-modal-provider'
2 | export * from './event-metadata-provider'
3 | export * from './event-tracking-provider'
4 | export * from './login-cta-modal-provider'
5 | export * from './triple-web'
6 |
--------------------------------------------------------------------------------
/packages/triple-web/src/providers/login-cta-modal-provider.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from 'react'
2 |
3 | import {
4 | LoginCtaModalContext,
5 | type LoginCtaModalContextValue,
6 | } from '../modal/login-cta-modal-context'
7 |
8 | export type LoginCtaModalProviderProps =
9 | PropsWithChildren
10 |
11 | export function LoginCtaModalProvider({
12 | children,
13 | showOptions,
14 | }: LoginCtaModalProviderProps) {
15 | return (
16 |
17 | {children}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './use-login'
3 | export * from './use-logout'
4 | export * from './use-session-availability'
5 | export * from './use-session-callback'
6 | export * from './use-session'
7 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/use-login.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useLogin
6 |
7 | 로그인 함수를 사용합니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const login = useLogin()
13 | ```
14 |
15 | ## Returns
16 |
17 | `(options?: LoginOptions) => void`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/use-logout.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useLogout
6 |
7 | 로그아웃 함수를 사용합니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const logout = useLogout()
13 | ```
14 |
15 | ## Returns
16 |
17 | `() => void`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/use-session-availability.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useSessionAvailability
6 |
7 | 세션이 유효한지 여부를 판별합니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const sessionAvailability = useSessionAvailability()
13 | ```
14 |
15 | ## Returns
16 |
17 | `boolean`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/use-session-availability.ts:
--------------------------------------------------------------------------------
1 | import { useSession } from './use-session'
2 |
3 | /**
4 | * 세션이 유효한지 여부를 판별합니다.
5 | */
6 | export function useSessionAvailability() {
7 | const { user } = useSession()
8 | return !!user
9 | }
10 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/use-session-callback.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useSessionCallback
6 |
7 | sessionId가 있는 환경에서만 주어진 콜백을 실행하는 함수를 반환하는 훅.
8 | sessionId가 없으면 로그인 유도 모달을 띄웁니다.
9 |
10 | ## Usage
11 |
12 | ```tsx
13 | const callback = useSessionCallback()
14 | ```
15 |
16 | ## Parameters
17 |
18 | ### fn
19 |
20 | `T extends (...args: any[]) => any`
21 |
22 | ## Returns
23 |
24 | `T`
25 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/use-session.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useSession
6 |
7 | SessionContext 값을 가져옵니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const session = useSession()
13 | ```
14 |
15 | ## Returns
16 |
17 | `SessionContext`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/use-session.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { SessionStateContext } from './context'
4 |
5 | /**
6 | * SessionContext 값을 가져옵니다.
7 | */
8 | export function useSession() {
9 | const context = useContext(SessionStateContext)
10 |
11 | if (context === undefined) {
12 | throw new Error('SessionContext가 없습니다.')
13 | }
14 |
15 | return context
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/src/session/utils/redirect.ts:
--------------------------------------------------------------------------------
1 | import { generateUrl } from '@titicaca/view-utilities'
2 | import qs from 'qs'
3 |
4 | export function getRedirectUrl(href: string) {
5 | const currentUrl = decodeURI(window.location.href)
6 |
7 | const redirectUrl = generateUrl(
8 | {
9 | query: qs.stringify({
10 | redirectUrl: currentUrl,
11 | }),
12 | },
13 | href,
14 | )
15 |
16 | return redirectUrl
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/user-agent/context.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 |
5 | import type { UserAgentValue } from './types'
6 |
7 | export const UserAgentContext = createContext(
8 | undefined,
9 | )
10 |
--------------------------------------------------------------------------------
/packages/triple-web/src/user-agent/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 | export * from './use-user-agent'
3 |
--------------------------------------------------------------------------------
/packages/triple-web/src/user-agent/types.ts:
--------------------------------------------------------------------------------
1 | import type { IResult } from 'ua-parser-js'
2 |
3 | export type UserAgentValue = IResult & { isMobile: boolean }
4 |
--------------------------------------------------------------------------------
/packages/triple-web/src/user-agent/use-user-agent.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, ArgTypes } from '@storybook/blocks'
2 |
3 |
4 |
5 | # useUserAgent
6 |
7 | UserAgentContext 값을 가져옵니다.
8 |
9 | ## Usage
10 |
11 | ```tsx
12 | const userAgent = useUserAgent()
13 | ```
14 |
15 | ## Returns
16 |
17 | `UserAgentContext`
18 |
--------------------------------------------------------------------------------
/packages/triple-web/src/user-agent/use-user-agent.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | import { UserAgentContext } from './context'
4 |
5 | /**
6 | * UserAgentContext 값을 가져옵니다.
7 | */
8 | export function useUserAgent() {
9 | const context = useContext(UserAgentContext)
10 |
11 | if (context === undefined) {
12 | throw new Error('UserAgentContext가 없습니다.')
13 | }
14 |
15 | return context
16 | }
17 |
--------------------------------------------------------------------------------
/packages/triple-web/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/triple-web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/triple-web/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/type-definitions/README.md:
--------------------------------------------------------------------------------
1 | # `@titicaca/type-definitions`
2 |
3 | 트리플 프론트엔드의 공통 타입을 모아놓는 패키지입니다.
4 |
--------------------------------------------------------------------------------
/packages/type-definitions/src/geojson.ts:
--------------------------------------------------------------------------------
1 | export interface PointGeoJson {
2 | type: 'Point'
3 | coordinates: [number, number]
4 | }
5 |
6 | export interface LatLngLiteral {
7 | lat: number
8 | lng: number
9 | }
10 |
--------------------------------------------------------------------------------
/packages/type-definitions/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './translated-property'
2 | export * from './listing-poi'
3 | export * from './image'
4 | export * from './geojson'
5 | export * from './inventory-item'
6 | export * from './triple-document'
7 |
--------------------------------------------------------------------------------
/packages/type-definitions/src/inventory-item.ts:
--------------------------------------------------------------------------------
1 | export interface InventoryItemMeta {
2 | id?: string
3 | image?: string
4 | desc?: string
5 | detailedDesc?: string
6 | target?: string
7 | text?: string
8 | }
9 |
--------------------------------------------------------------------------------
/packages/type-definitions/src/translated-property.ts:
--------------------------------------------------------------------------------
1 | export interface TranslatedProperty {
2 | primary?: string | null
3 | ko?: string | null
4 | en?: string | null
5 | ja?: string | null
6 | zh?: string | null
7 | local?: string | null
8 | }
9 |
--------------------------------------------------------------------------------
/packages/type-definitions/src/triple-document.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * guestMode가 undefined가 아닌 경우, 로그인이 필요한 동작(스크랩, 리뷰쓰기)등이 불가능하며, 앱으로 연결되는 루트를 차단합니다.
3 | * 로그인 없이 triple-document를 사용하는 페이지를 사용자가 탐색할 수 있도록 할 때 사용합니다(예. 외부 이벤트에 POI 상세페이지 제공).
4 | * - 'seoul-con' : '서울콘' 행사. triple-content-web에서 가이드와 POI 영어 컨텐트를 제공합니다.
5 | */
6 | export type GuestModeType = 'seoul-con'
7 |
--------------------------------------------------------------------------------
/packages/type-definitions/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/type-definitions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/type-definitions/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/packages/view-utilities/src/format-number.ts:
--------------------------------------------------------------------------------
1 | export function formatNumber(
2 | number: number | string | null | undefined,
3 | ): string {
4 | if (typeof number === 'number' || typeof number === 'string') {
5 | const [integer, ...fractions] = number.toString().split('.')
6 | return [integer.replace(/\B(?=(\d{3})+(?!\d))/g, ','), ...fractions].join(
7 | '.',
8 | )
9 | }
10 |
11 | return ''
12 | }
13 |
--------------------------------------------------------------------------------
/packages/view-utilities/src/generate-deep-link/README.md:
--------------------------------------------------------------------------------
1 | # generate-deep-link
2 |
3 | ## 사용 방법
4 |
5 | ```:javascript
6 | const generateDeepLink = makeDeepLinkGenerator({
7 | oneLinkParams: {
8 | subdomain: '',
9 | id: '',
10 | pid: '',
11 | },
12 | appScheme: '',
13 | webURLBase: '',
14 | })
15 |
16 | // 컴포넌트에서
17 | generateDeepLink({
18 | campaign: '하단 배너',
19 | pid: fromSearchAd ? 'searched': undefined,
20 | ...injectContentSource(source),
21 | ...injectUTMContext(utmContext),
22 | })
23 | ```
24 |
--------------------------------------------------------------------------------
/packages/view-utilities/src/generate-deep-link/index.ts:
--------------------------------------------------------------------------------
1 | export * from './make-deep-link-generator'
2 | export * from './param-injectors'
3 |
--------------------------------------------------------------------------------
/packages/view-utilities/src/generate-share-image-url.ts:
--------------------------------------------------------------------------------
1 | const transformations: { [key: string]: string } = {
2 | full: 'c_limit,h_2048,w_2048',
3 | large: 'c_limit,h_1024,w_1024',
4 | small: 'c_fill,h_256,w_256',
5 | }
6 |
7 | export function generateShareImageUrl({
8 | mediaBaseUrl,
9 | cloudinaryId,
10 | version,
11 | }: {
12 | mediaBaseUrl: string
13 | cloudinaryId: string
14 | version: string
15 | }): string {
16 | const transformation = transformations[version] || transformations.large
17 | return `${mediaBaseUrl}/${transformation}/${cloudinaryId}.jpeg`
18 | }
19 |
--------------------------------------------------------------------------------
/packages/view-utilities/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './generate-deep-link'
2 | export * from './normalize-query-keys'
3 | export * from './routelist'
4 | export * from './strict-query'
5 | export * from './debounce'
6 | export * from './derive-current-state-and-count'
7 | export * from './find-folded-position'
8 | export * from './format-number'
9 | export * from './generate-share-image-url'
10 | export * from './measure-distance'
11 | export * from './timestamp'
12 | export * from './url'
13 |
--------------------------------------------------------------------------------
/packages/view-utilities/src/measure-distance.spec.ts:
--------------------------------------------------------------------------------
1 | import { measureDistance } from './measure-distance'
2 |
3 | describe('measureDistance', () => {
4 | it('직선거리', () => {
5 | const distance = measureDistance(
6 | {
7 | coordinates: [121.525966, 25.094853],
8 | type: 'Point',
9 | },
10 | {
11 | coordinates: [121.528408, 25.095141],
12 | type: 'Point',
13 | },
14 | )
15 |
16 | expect(distance).toBe(248)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/packages/view-utilities/src/routelist/index.ts:
--------------------------------------------------------------------------------
1 | export * from './routelist'
2 |
--------------------------------------------------------------------------------
/packages/view-utilities/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | "noEmitOnError": true,
7 | "outDir": "./lib"
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": [
11 | "node_modules",
12 | "lib",
13 | "src/**/*.test.*",
14 | "src/**/*.spec.*",
15 | "src/**/*.stories.*"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/view-utilities/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["node_modules", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/view-utilities/vite.config.mts:
--------------------------------------------------------------------------------
1 | export { default } from '../../vite.config.mjs'
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'examples/*'
3 | - 'packages/*'
4 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "packageRules": [
4 | {
5 | "matchManagers": ["npm"],
6 | "matchDepTypes": ["engines"],
7 | "enabled": false
8 | },
9 | {
10 | "matchSourceUrls": ["https://github.com/titicacadev/triple-frontend"],
11 | "enabled": false
12 | },
13 | {
14 | "groupName": "SWC",
15 | "matchPackageNames": ["@swc/*"]
16 | },
17 | {
18 | "matchPackageNames": ["firebase", "nx-cloud"],
19 | "enabled": false
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/stories/introduction.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/blocks'
2 | import lernaConfig from '../lerna.json'
3 |
4 |
5 |
6 | # Introduction
7 |
8 | `Triple Frontend`는 트리플 프론트엔드 공용 컴포넌트 및 라이브러리입니다.
9 |
10 | ## 버전
11 |
12 | {lernaConfig.version}
13 |
14 | ## 기여하기
15 |
16 | [CONTRIBUTING.md](https://github.com/titicacadev/triple-frontend/blob/main/CONTRIBUTING.md)를 참고해주세요.
17 |
18 | ## 라이선스
19 |
20 | [MIT License](https://github.com/titicacadev/triple-frontend/blob/main/LICENSE)
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["DOM", "DOM.Iterable", "ES2015"],
4 | "module": "ESNext",
5 | "moduleResolution": "Bundler",
6 | "target": "ES2015",
7 |
8 | "allowJs": true,
9 | "allowSyntheticDefaultImports": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "jsx": "react-jsx",
12 | "noFallthroughCasesInSwitch": true,
13 | "resolveJsonModule": true,
14 | "skipLibCheck": true,
15 | "strict": true
16 | },
17 | "include": ["global.d.ts", "**/*.ts", "**/*.tsx"],
18 | "exclude": ["**/node_modules", "**/lib"]
19 | }
20 |
--------------------------------------------------------------------------------