create-weave-frontend-app 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/chunk-HZJMO45D.js +437 -0
- package/dist/create-app.d.ts +14 -0
- package/dist/create-app.js +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +106 -0
- package/package.json +65 -0
- package/template/+nextjs+azure-web-pubsub/README.md +36 -0
- package/template/+nextjs+azure-web-pubsub/api/del-image.ts +8 -0
- package/template/+nextjs+azure-web-pubsub/api/get-images.ts +15 -0
- package/template/+nextjs+azure-web-pubsub/api/post-image.ts +14 -0
- package/template/+nextjs+azure-web-pubsub/api/post-remove-background.ts +10 -0
- package/template/+nextjs+azure-web-pubsub/app/error/page.tsx +10 -0
- package/template/+nextjs+azure-web-pubsub/app/favicon.ico +0 -0
- package/template/+nextjs+azure-web-pubsub/app/globals.css +193 -0
- package/template/+nextjs+azure-web-pubsub/app/layout.tsx +46 -0
- package/template/+nextjs+azure-web-pubsub/app/page.tsx +7 -0
- package/template/+nextjs+azure-web-pubsub/app/providers.tsx +18 -0
- package/template/+nextjs+azure-web-pubsub/app/room/[roomId]/page.tsx +5 -0
- package/template/+nextjs+azure-web-pubsub/assets/images/home.png +0 -0
- package/template/+nextjs+azure-web-pubsub/assets/images/logo.png +0 -0
- package/template/+nextjs+azure-web-pubsub/components/actions/align-elements-tool/align-elements-tool.ts +94 -0
- package/template/+nextjs+azure-web-pubsub/components/actions/color-token-tool/color-token-tool.ts +164 -0
- package/template/+nextjs+azure-web-pubsub/components/actions/color-token-tool/constants.ts +5 -0
- package/template/+nextjs+azure-web-pubsub/components/actions/color-token-tool/types.ts +12 -0
- package/template/+nextjs+azure-web-pubsub/components/error/error.tsx +62 -0
- package/template/+nextjs+azure-web-pubsub/components/error/errors.ts +35 -0
- package/template/+nextjs+azure-web-pubsub/components/home/home.tsx +92 -0
- package/template/+nextjs+azure-web-pubsub/components/home-components/home-showcase-animation.tsx +119 -0
- package/template/+nextjs+azure-web-pubsub/components/home-components/login-form.tsx +117 -0
- package/template/+nextjs+azure-web-pubsub/components/nodes/color-token/color-token.ts +171 -0
- package/template/+nextjs+azure-web-pubsub/components/room/room.layout.tsx +115 -0
- package/template/+nextjs+azure-web-pubsub/components/room/room.tsx +125 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/color-tokens-library/color-token.tsx +31 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/color-tokens-library/color-tokens-library.tsx +64 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/connected-users.tsx +152 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/connection-status.tsx +52 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/context-menu.tsx +152 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/frames-library/frames-library.image.tsx +48 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/frames-library/frames-library.presentation-image.tsx +61 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/frames-library/frames-library.tsx +316 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/frames-library/utils.ts +27 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-arrange.tsx +69 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-drawer.tsx +140 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-edit.tsx +80 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-selection.tsx +30 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-shortcut-element.tsx +24 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-tools.tsx +89 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-view.tsx +30 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/help-zoom.tsx +46 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/help/shortcut-element.tsx +42 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/hooks/use-context-menu.tsx +514 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/hooks/use-get-azure-web-pubsub-provider.ts +78 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/hooks/use-get-os.ts +12 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/hooks/use-get-weave-js-props.tsx +120 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/hooks/use-handle-route-params.ts +30 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/hooks/use-key-down.ts +29 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/hooks/use-keyboard-handler.tsx +557 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/images-library/images-library.tsx +146 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/inputs/input-color.tsx +101 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/inputs/input-font-family.tsx +99 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/inputs/input-number.tsx +61 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/inputs/input-text.tsx +51 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/inputs/number-input.tsx +107 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/appearance-properties.tsx +119 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/color-token-properties.tsx +108 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/crop-properties.tsx +156 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/fill-properties.tsx +115 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/frame-properties.tsx +100 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/image-properties.tsx +57 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/position-properties.tsx +156 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/size-properties.tsx +131 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/stroke-properties.tsx +327 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/node-properties/text-properties.tsx +467 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/multiuse-overlay.tsx +127 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/node-properties.tsx +98 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/overlay-animation-wrapper.tsx +31 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/room-information-overlay.tsx +247 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/room-users-overlay.tsx +31 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/tools-overlay.tsx +289 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/variants.ts +58 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/overlay/zoom-handler-overlay.tsx +447 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/room-error.tsx +37 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/room-loader/room-loader.tsx +98 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/selection-information.tsx +74 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/toggle-icon-button.tsx +60 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/toolbar/toolbar-button.tsx +60 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/toolbar/toolbar-toggle-button.tsx +40 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/toolbar/toolbar.tsx +28 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/upload-file.tsx +130 -0
- package/template/+nextjs+azure-web-pubsub/components/room-components/with-instance-node.tsx +53 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/accordion.tsx +66 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/avatar.tsx +53 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/button.tsx +58 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/card.tsx +68 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/checkbox.tsx +32 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/color-picker-component.tsx +69 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/context/color-picker-context.tsx +28 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/editor/color-picker-format-editor.tsx +34 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/index.ts +7 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/selector/color-picker-alpha.tsx +79 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/selector/color-picker-eyedropper.tsx +95 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/selector/color-picker-format-selector.tsx +50 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/selector/color-picker-hue.tsx +67 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/selector/color-picker-saturation.tsx +145 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/text-inputs/color-picker-alpha-percentage.tsx +60 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/text-inputs/color-picker-hexa.tsx +65 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/text-inputs/color-picker-rgba.tsx +62 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/command.tsx +177 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/dialog.tsx +135 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/drawer.tsx +132 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/dropdown-menu.tsx +201 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/form.tsx +167 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/input.tsx +21 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/label.tsx +24 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/popover.tsx +48 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/reactbits/Backgrounds/Dither/Dither.tsx +350 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/reactbits/Backgrounds/Threads/Threads.tsx +239 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/reactbits/TextAnimations/RotatingText/RotatingText.tsx +276 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/scroll-area.tsx +58 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/select.tsx +185 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/sheet.tsx +139 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/sonner.tsx +25 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/tabs.tsx +66 -0
- package/template/+nextjs+azure-web-pubsub/components/ui/tooltip.tsx +61 -0
- package/template/+nextjs+azure-web-pubsub/components/utils/constants.ts +118 -0
- package/template/+nextjs+azure-web-pubsub/components/utils/logo.tsx +34 -0
- package/template/+nextjs+azure-web-pubsub/components.json +21 -0
- package/template/+nextjs+azure-web-pubsub/example.env +2 -0
- package/template/+nextjs+azure-web-pubsub/example.gitignore +44 -0
- package/template/+nextjs+azure-web-pubsub/jsrepo.json +11 -0
- package/template/+nextjs+azure-web-pubsub/lib/utils.ts +43 -0
- package/template/+nextjs+azure-web-pubsub/new-types.d.ts +8 -0
- package/template/+nextjs+azure-web-pubsub/next-env.d.ts +5 -0
- package/template/+nextjs+azure-web-pubsub/next.config.js +52 -0
- package/template/+nextjs+azure-web-pubsub/postcss.config.mjs +5 -0
- package/template/+nextjs+azure-web-pubsub/store/store.ts +241 -0
- package/template/+nextjs+azure-web-pubsub/tsconfig.json +37 -0
- package/template/+nextjs+azure-web-pubsub/vitest.config.mts +10 -0
- package/template/+nextjs+websockets/README.md +39 -0
- package/template/+nextjs+websockets/api/del-image.ts +8 -0
- package/template/+nextjs+websockets/api/get-images.ts +15 -0
- package/template/+nextjs+websockets/api/post-image.ts +14 -0
- package/template/+nextjs+websockets/api/post-remove-background.ts +10 -0
- package/template/+nextjs+websockets/app/error/page.tsx +10 -0
- package/template/+nextjs+websockets/app/favicon.ico +0 -0
- package/template/+nextjs+websockets/app/globals.css +193 -0
- package/template/+nextjs+websockets/app/layout.tsx +46 -0
- package/template/+nextjs+websockets/app/page.tsx +7 -0
- package/template/+nextjs+websockets/app/providers.tsx +18 -0
- package/template/+nextjs+websockets/app/room/[roomId]/page.tsx +5 -0
- package/template/+nextjs+websockets/assets/images/home.png +0 -0
- package/template/+nextjs+websockets/assets/images/logo.png +0 -0
- package/template/+nextjs+websockets/components/actions/align-elements-tool/align-elements-tool.ts +94 -0
- package/template/+nextjs+websockets/components/actions/color-token-tool/color-token-tool.ts +164 -0
- package/template/+nextjs+websockets/components/actions/color-token-tool/constants.ts +5 -0
- package/template/+nextjs+websockets/components/actions/color-token-tool/types.ts +12 -0
- package/template/+nextjs+websockets/components/error/error.tsx +62 -0
- package/template/+nextjs+websockets/components/error/errors.ts +35 -0
- package/template/+nextjs+websockets/components/home/home.tsx +92 -0
- package/template/+nextjs+websockets/components/home-components/home-showcase-animation.tsx +119 -0
- package/template/+nextjs+websockets/components/home-components/login-form.tsx +117 -0
- package/template/+nextjs+websockets/components/nodes/color-token/color-token.ts +171 -0
- package/template/+nextjs+websockets/components/room/room.layout.tsx +115 -0
- package/template/+nextjs+websockets/components/room/room.tsx +125 -0
- package/template/+nextjs+websockets/components/room-components/color-tokens-library/color-token.tsx +31 -0
- package/template/+nextjs+websockets/components/room-components/color-tokens-library/color-tokens-library.tsx +64 -0
- package/template/+nextjs+websockets/components/room-components/connected-users.tsx +152 -0
- package/template/+nextjs+websockets/components/room-components/connection-status.tsx +52 -0
- package/template/+nextjs+websockets/components/room-components/context-menu.tsx +152 -0
- package/template/+nextjs+websockets/components/room-components/frames-library/frames-library.image.tsx +48 -0
- package/template/+nextjs+websockets/components/room-components/frames-library/frames-library.presentation-image.tsx +61 -0
- package/template/+nextjs+websockets/components/room-components/frames-library/frames-library.tsx +316 -0
- package/template/+nextjs+websockets/components/room-components/frames-library/utils.ts +27 -0
- package/template/+nextjs+websockets/components/room-components/help/help-arrange.tsx +69 -0
- package/template/+nextjs+websockets/components/room-components/help/help-drawer.tsx +140 -0
- package/template/+nextjs+websockets/components/room-components/help/help-edit.tsx +80 -0
- package/template/+nextjs+websockets/components/room-components/help/help-selection.tsx +30 -0
- package/template/+nextjs+websockets/components/room-components/help/help-shortcut-element.tsx +24 -0
- package/template/+nextjs+websockets/components/room-components/help/help-tools.tsx +89 -0
- package/template/+nextjs+websockets/components/room-components/help/help-view.tsx +30 -0
- package/template/+nextjs+websockets/components/room-components/help/help-zoom.tsx +46 -0
- package/template/+nextjs+websockets/components/room-components/help/shortcut-element.tsx +42 -0
- package/template/+nextjs+websockets/components/room-components/hooks/use-context-menu.tsx +514 -0
- package/template/+nextjs+websockets/components/room-components/hooks/use-get-os.ts +12 -0
- package/template/+nextjs+websockets/components/room-components/hooks/use-get-weave-js-props.tsx +120 -0
- package/template/+nextjs+websockets/components/room-components/hooks/use-get-websockets-provider.ts +79 -0
- package/template/+nextjs+websockets/components/room-components/hooks/use-handle-route-params.ts +30 -0
- package/template/+nextjs+websockets/components/room-components/hooks/use-key-down.ts +29 -0
- package/template/+nextjs+websockets/components/room-components/hooks/use-keyboard-handler.tsx +557 -0
- package/template/+nextjs+websockets/components/room-components/images-library/images-library.tsx +146 -0
- package/template/+nextjs+websockets/components/room-components/inputs/input-color.tsx +101 -0
- package/template/+nextjs+websockets/components/room-components/inputs/input-font-family.tsx +99 -0
- package/template/+nextjs+websockets/components/room-components/inputs/input-number.tsx +61 -0
- package/template/+nextjs+websockets/components/room-components/inputs/input-text.tsx +51 -0
- package/template/+nextjs+websockets/components/room-components/inputs/number-input.tsx +107 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/appearance-properties.tsx +119 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/color-token-properties.tsx +108 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/crop-properties.tsx +156 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/fill-properties.tsx +115 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/frame-properties.tsx +100 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/image-properties.tsx +57 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/position-properties.tsx +156 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/size-properties.tsx +131 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/stroke-properties.tsx +327 -0
- package/template/+nextjs+websockets/components/room-components/node-properties/text-properties.tsx +467 -0
- package/template/+nextjs+websockets/components/room-components/overlay/multiuse-overlay.tsx +127 -0
- package/template/+nextjs+websockets/components/room-components/overlay/node-properties.tsx +98 -0
- package/template/+nextjs+websockets/components/room-components/overlay/overlay-animation-wrapper.tsx +31 -0
- package/template/+nextjs+websockets/components/room-components/overlay/room-information-overlay.tsx +247 -0
- package/template/+nextjs+websockets/components/room-components/overlay/room-users-overlay.tsx +31 -0
- package/template/+nextjs+websockets/components/room-components/overlay/tools-overlay.tsx +289 -0
- package/template/+nextjs+websockets/components/room-components/overlay/variants.ts +58 -0
- package/template/+nextjs+websockets/components/room-components/overlay/zoom-handler-overlay.tsx +447 -0
- package/template/+nextjs+websockets/components/room-components/room-error.tsx +37 -0
- package/template/+nextjs+websockets/components/room-components/room-loader/room-loader.tsx +98 -0
- package/template/+nextjs+websockets/components/room-components/selection-information.tsx +74 -0
- package/template/+nextjs+websockets/components/room-components/toggle-icon-button.tsx +60 -0
- package/template/+nextjs+websockets/components/room-components/toolbar/toolbar-button.tsx +60 -0
- package/template/+nextjs+websockets/components/room-components/toolbar/toolbar-toggle-button.tsx +40 -0
- package/template/+nextjs+websockets/components/room-components/toolbar/toolbar.tsx +28 -0
- package/template/+nextjs+websockets/components/room-components/upload-file.tsx +130 -0
- package/template/+nextjs+websockets/components/room-components/with-instance-node.tsx +53 -0
- package/template/+nextjs+websockets/components/ui/accordion.tsx +66 -0
- package/template/+nextjs+websockets/components/ui/avatar.tsx +53 -0
- package/template/+nextjs+websockets/components/ui/button.tsx +58 -0
- package/template/+nextjs+websockets/components/ui/card.tsx +68 -0
- package/template/+nextjs+websockets/components/ui/checkbox.tsx +32 -0
- package/template/+nextjs+websockets/components/ui/color-picker/color-picker-component.tsx +69 -0
- package/template/+nextjs+websockets/components/ui/color-picker/context/color-picker-context.tsx +28 -0
- package/template/+nextjs+websockets/components/ui/color-picker/editor/color-picker-format-editor.tsx +34 -0
- package/template/+nextjs+websockets/components/ui/color-picker/index.ts +7 -0
- package/template/+nextjs+websockets/components/ui/color-picker/selector/color-picker-alpha.tsx +79 -0
- package/template/+nextjs+websockets/components/ui/color-picker/selector/color-picker-eyedropper.tsx +95 -0
- package/template/+nextjs+websockets/components/ui/color-picker/selector/color-picker-format-selector.tsx +50 -0
- package/template/+nextjs+websockets/components/ui/color-picker/selector/color-picker-hue.tsx +67 -0
- package/template/+nextjs+websockets/components/ui/color-picker/selector/color-picker-saturation.tsx +145 -0
- package/template/+nextjs+websockets/components/ui/color-picker/text-inputs/color-picker-alpha-percentage.tsx +60 -0
- package/template/+nextjs+websockets/components/ui/color-picker/text-inputs/color-picker-hexa.tsx +65 -0
- package/template/+nextjs+websockets/components/ui/color-picker/text-inputs/color-picker-rgba.tsx +62 -0
- package/template/+nextjs+websockets/components/ui/command.tsx +177 -0
- package/template/+nextjs+websockets/components/ui/dialog.tsx +135 -0
- package/template/+nextjs+websockets/components/ui/drawer.tsx +132 -0
- package/template/+nextjs+websockets/components/ui/dropdown-menu.tsx +201 -0
- package/template/+nextjs+websockets/components/ui/form.tsx +167 -0
- package/template/+nextjs+websockets/components/ui/input.tsx +21 -0
- package/template/+nextjs+websockets/components/ui/label.tsx +24 -0
- package/template/+nextjs+websockets/components/ui/popover.tsx +48 -0
- package/template/+nextjs+websockets/components/ui/reactbits/Backgrounds/Dither/Dither.tsx +350 -0
- package/template/+nextjs+websockets/components/ui/reactbits/Backgrounds/Threads/Threads.tsx +239 -0
- package/template/+nextjs+websockets/components/ui/reactbits/TextAnimations/RotatingText/RotatingText.tsx +276 -0
- package/template/+nextjs+websockets/components/ui/scroll-area.tsx +58 -0
- package/template/+nextjs+websockets/components/ui/select.tsx +185 -0
- package/template/+nextjs+websockets/components/ui/sheet.tsx +139 -0
- package/template/+nextjs+websockets/components/ui/sonner.tsx +25 -0
- package/template/+nextjs+websockets/components/ui/tabs.tsx +66 -0
- package/template/+nextjs+websockets/components/ui/tooltip.tsx +61 -0
- package/template/+nextjs+websockets/components/utils/constants.ts +118 -0
- package/template/+nextjs+websockets/components/utils/logo.tsx +34 -0
- package/template/+nextjs+websockets/components.json +21 -0
- package/template/+nextjs+websockets/example.env +2 -0
- package/template/+nextjs+websockets/example.gitignore +44 -0
- package/template/+nextjs+websockets/jsrepo.json +11 -0
- package/template/+nextjs+websockets/lib/utils.ts +43 -0
- package/template/+nextjs+websockets/new-types.d.ts +8 -0
- package/template/+nextjs+websockets/next-env.d.ts +5 -0
- package/template/+nextjs+websockets/next.config.js +52 -0
- package/template/+nextjs+websockets/postcss.config.mjs +5 -0
- package/template/+nextjs+websockets/store/store.ts +241 -0
- package/template/+nextjs+websockets/tsconfig.json +37 -0
- package/template/+nextjs+websockets/vitest.config.mts +10 -0
- package/template/package.json +81 -0
package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/color-picker-component.tsx
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Color, { ColorInstance } from "color";
|
|
2
|
+
import { HTMLAttributes, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { ColorPickerContext } from "./context/color-picker-context";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
export type ColorPickerProps = HTMLAttributes<HTMLDivElement> & {
|
|
7
|
+
/** The current color value in #RRGGBBAA format */
|
|
8
|
+
value?: string;
|
|
9
|
+
/** The default color value in #RRGGBBAA format */
|
|
10
|
+
defaultValue?: string;
|
|
11
|
+
onChange?: (value: string) => void;
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* ColorPicker component
|
|
17
|
+
* A comprehensive color picker that supports various color formats
|
|
18
|
+
*/
|
|
19
|
+
export const ColorPicker = ({
|
|
20
|
+
value,
|
|
21
|
+
onChange,
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}: ColorPickerProps) => {
|
|
25
|
+
const initialColor = Color(value);
|
|
26
|
+
|
|
27
|
+
const [color, setColor] = useState<ColorInstance>(initialColor);
|
|
28
|
+
const [mode, setMode] = useState("hex");
|
|
29
|
+
|
|
30
|
+
const lastValue = useRef(Color(initialColor).hexa());
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (Color(value).hexa() !== lastValue.current) {
|
|
34
|
+
const externalColor = Color(value);
|
|
35
|
+
setColor(externalColor);
|
|
36
|
+
lastValue.current = externalColor.hexa();
|
|
37
|
+
}
|
|
38
|
+
}, [value]);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (onChange) {
|
|
42
|
+
const newColor = Color(color);
|
|
43
|
+
if (lastValue.current !== newColor.hexa()) {
|
|
44
|
+
onChange(newColor.hexa());
|
|
45
|
+
lastValue.current = newColor.hexa();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, [color, onChange]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<ColorPickerContext.Provider
|
|
52
|
+
value={{
|
|
53
|
+
color,
|
|
54
|
+
mode,
|
|
55
|
+
setColor,
|
|
56
|
+
setMode,
|
|
57
|
+
isUpdating: false,
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<div
|
|
61
|
+
className={cn(
|
|
62
|
+
"grid w-full gap-4 rounded-none transition-opacity duration-150",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
</ColorPickerContext.Provider>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ColorInstance } from "color";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
|
|
4
|
+
interface ColorPickerContextValue {
|
|
5
|
+
mode: string;
|
|
6
|
+
setColor: (color: ColorInstance) => void;
|
|
7
|
+
setMode: (mode: string) => void;
|
|
8
|
+
color: ColorInstance;
|
|
9
|
+
isUpdating: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ColorPickerContext = createContext<ColorPickerContextValue | undefined>(
|
|
13
|
+
undefined
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hook to access the ColorPicker context
|
|
18
|
+
* Must be used within a ColorPicker component
|
|
19
|
+
*/
|
|
20
|
+
export const useColorPicker = () => {
|
|
21
|
+
const context = useContext(ColorPickerContext);
|
|
22
|
+
|
|
23
|
+
if (!context) {
|
|
24
|
+
throw new Error("useColorPicker must be used within a ColorPickerProvider");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return context;
|
|
28
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HTMLAttributes, useMemo } from "react";
|
|
2
|
+
import { ColorPickerHexaInput } from "../text-inputs/color-picker-hexa";
|
|
3
|
+
import { useColorPicker } from "../context/color-picker-context";
|
|
4
|
+
import { ColorPickerRGBAInput } from "../text-inputs/color-picker-rgba";
|
|
5
|
+
|
|
6
|
+
export type ColorPickerFormatEditorProps = HTMLAttributes<HTMLDivElement>;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ColorPickerFormatEditor component
|
|
11
|
+
* Displays and allows editing of the color value in the selected format
|
|
12
|
+
*/
|
|
13
|
+
export const ColorPickerFormatEditor = ({
|
|
14
|
+
className,
|
|
15
|
+
...props
|
|
16
|
+
}: ColorPickerFormatEditorProps) => {
|
|
17
|
+
const { mode } = useColorPicker();
|
|
18
|
+
const component = useMemo(() => {
|
|
19
|
+
let component;
|
|
20
|
+
switch (mode) {
|
|
21
|
+
case "hex":
|
|
22
|
+
component = <ColorPickerHexaInput {...props} className={className} />;
|
|
23
|
+
break;
|
|
24
|
+
case "rgba":
|
|
25
|
+
component = <ColorPickerRGBAInput {...props} className={className} />;
|
|
26
|
+
break;
|
|
27
|
+
default:
|
|
28
|
+
component = <ColorPickerHexaInput {...props} className={className} />;
|
|
29
|
+
}
|
|
30
|
+
return component;
|
|
31
|
+
}, [className, mode, props]);
|
|
32
|
+
|
|
33
|
+
return <>{component}</>;
|
|
34
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./color-picker-component";
|
|
2
|
+
export * from "./selector/color-picker-alpha";
|
|
3
|
+
export * from "./selector/color-picker-hue";
|
|
4
|
+
export * from "./selector/color-picker-saturation";
|
|
5
|
+
export * from "./selector/color-picker-eyedropper";
|
|
6
|
+
export * from "./editor/color-picker-format-editor";
|
|
7
|
+
export * from "./selector/color-picker-format-selector";
|
package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/selector/color-picker-alpha.tsx
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Range, Root, Thumb, Track } from "@radix-ui/react-slider";
|
|
4
|
+
import {
|
|
5
|
+
type HTMLAttributes,
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from "react";
|
|
12
|
+
import { useColorPicker } from "../context/color-picker-context";
|
|
13
|
+
|
|
14
|
+
export type ColorPickerAlphaProps = HTMLAttributes<HTMLDivElement>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ColorPickerAlpha component
|
|
18
|
+
* A slider for selecting the alpha (transparency) value
|
|
19
|
+
*/
|
|
20
|
+
export const ColorPickerAlpha = ({
|
|
21
|
+
className,
|
|
22
|
+
dir,
|
|
23
|
+
defaultValue,
|
|
24
|
+
...props
|
|
25
|
+
}: ColorPickerAlphaProps) => {
|
|
26
|
+
const { color, setColor } = useColorPicker();
|
|
27
|
+
const [actualValue, setActualValue] = useState<number>(color.alpha() * 100);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setActualValue(color.alpha() * 100);
|
|
31
|
+
}, [color]);
|
|
32
|
+
|
|
33
|
+
const solidColor = useMemo(() => color.alpha(1).rgb().string(), [color]);
|
|
34
|
+
|
|
35
|
+
const lastAlpha = useRef(color.alpha() * 100);
|
|
36
|
+
|
|
37
|
+
const onValueChange = useCallback(
|
|
38
|
+
([newAlpha]: number[]) => {
|
|
39
|
+
if (newAlpha !== lastAlpha.current) {
|
|
40
|
+
const newColor = color.alpha(newAlpha / 100);
|
|
41
|
+
setColor(newColor);
|
|
42
|
+
lastAlpha.current = newAlpha;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
[color, setColor]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Root
|
|
50
|
+
value={[actualValue]}
|
|
51
|
+
max={100}
|
|
52
|
+
step={1}
|
|
53
|
+
className={cn(
|
|
54
|
+
"relative flex h-5 w-full touch-none items-center transition-opacity duration-200",
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
onValueChange={onValueChange}
|
|
58
|
+
aria-label="Opacity"
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
<Track
|
|
62
|
+
className="relative my-0.5 h-3 w-full grow overflow-hidden rounded-full"
|
|
63
|
+
style={{
|
|
64
|
+
background:
|
|
65
|
+
'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") left center',
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<div
|
|
69
|
+
className="absolute inset-0 rounded-full transition-all duration-200"
|
|
70
|
+
style={{
|
|
71
|
+
background: `linear-gradient(90deg, transparent, ${solidColor})`,
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
<Range className="absolute h-full rounded-full bg-transparent" />
|
|
75
|
+
</Track>
|
|
76
|
+
<Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:h-5 hover:w-5" />
|
|
77
|
+
</Root>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Button } from "@/components/ui/button";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import Color from "color";
|
|
4
|
+
import { PipetteIcon } from "lucide-react";
|
|
5
|
+
import {
|
|
6
|
+
type ComponentProps,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useState,
|
|
10
|
+
} from "react";
|
|
11
|
+
import { useColorPicker } from "../context/color-picker-context";
|
|
12
|
+
|
|
13
|
+
export type ColorPickerEyeDropperProps = ComponentProps<typeof Button>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* ColorPickerEyeDropper component
|
|
17
|
+
* A button that activates the browser's eyedropper tool
|
|
18
|
+
*/
|
|
19
|
+
export const ColorPickerEyeDropper = ({
|
|
20
|
+
className,
|
|
21
|
+
...props
|
|
22
|
+
}: ColorPickerEyeDropperProps) => {
|
|
23
|
+
const { setColor } = useColorPicker();
|
|
24
|
+
const [isActive, setIsActive] = useState(false);
|
|
25
|
+
const [isSupported, setIsSupported] = useState(true);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
// @ts-expect-error - EyeDropper API is experimental
|
|
29
|
+
setIsSupported(typeof EyeDropper !== "undefined");
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
const handleEyeDropper = useCallback(async () => {
|
|
33
|
+
if (!isSupported) {
|
|
34
|
+
console.warn("EyeDropper API is not supported in this browser");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
setIsActive(true);
|
|
40
|
+
|
|
41
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
42
|
+
|
|
43
|
+
// @ts-expect-error - EyeDropper API is experimental
|
|
44
|
+
const eyeDropper = new EyeDropper();
|
|
45
|
+
const result = await eyeDropper.open();
|
|
46
|
+
|
|
47
|
+
const color = Color(result.sRGBHex);
|
|
48
|
+
|
|
49
|
+
const [h, s, l] = color.hsl().array();
|
|
50
|
+
|
|
51
|
+
const newColor = Color.hsl(h, s, l).alpha(100);
|
|
52
|
+
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
setColor(newColor);
|
|
55
|
+
}, 50);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (error instanceof Error && !error.message.includes("aborted")) {
|
|
58
|
+
console.error("EyeDropper failed:", error);
|
|
59
|
+
}
|
|
60
|
+
} finally {
|
|
61
|
+
setTimeout(() => setIsActive(false), 100);
|
|
62
|
+
}
|
|
63
|
+
}, [isSupported, setColor]);
|
|
64
|
+
|
|
65
|
+
if (!isSupported) {
|
|
66
|
+
return null; // Don't render if not supported
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="relative">
|
|
71
|
+
<Button
|
|
72
|
+
variant="outline"
|
|
73
|
+
size="icon"
|
|
74
|
+
onClick={handleEyeDropper}
|
|
75
|
+
disabled={isActive}
|
|
76
|
+
title="Pick color from screen"
|
|
77
|
+
aria-label="Pick color from screen"
|
|
78
|
+
className={cn(
|
|
79
|
+
"rounded-none shrink-0 text-muted-foreground",
|
|
80
|
+
isActive && "opacity-50 cursor-not-allowed",
|
|
81
|
+
className
|
|
82
|
+
)}
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
<PipetteIcon size={16} />
|
|
86
|
+
</Button>
|
|
87
|
+
{isActive && (
|
|
88
|
+
<div
|
|
89
|
+
className="fixed inset-0 z-50 bg-transparent cursor-crosshair"
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Select,
|
|
3
|
+
SelectContent,
|
|
4
|
+
SelectItem,
|
|
5
|
+
SelectTrigger,
|
|
6
|
+
SelectValue,
|
|
7
|
+
} from "@/components/ui/select";
|
|
8
|
+
import { cn } from "@/lib/utils";
|
|
9
|
+
import {
|
|
10
|
+
type ComponentProps,
|
|
11
|
+
} from "react";
|
|
12
|
+
import { useColorPicker } from "../context/color-picker-context";
|
|
13
|
+
|
|
14
|
+
export type ColorPickerFormatSelectorProps = ComponentProps<typeof SelectTrigger>;
|
|
15
|
+
|
|
16
|
+
const formats = ["hex", "rgba"];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ColorPickerFormatSelector component
|
|
20
|
+
* A dropdown for selecting the color format
|
|
21
|
+
*/
|
|
22
|
+
export const ColorPickerFormatSelector = ({
|
|
23
|
+
className,
|
|
24
|
+
...props
|
|
25
|
+
}: ColorPickerFormatSelectorProps) => {
|
|
26
|
+
const { mode, setMode } = useColorPicker();
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Select value={mode} onValueChange={setMode}>
|
|
30
|
+
<SelectTrigger
|
|
31
|
+
className={cn(
|
|
32
|
+
"h-8 !text-xs rounded-none font-normal text-gray-700",
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
data-size="sm"
|
|
36
|
+
aria-label="Color format"
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
<SelectValue placeholder="Format" />
|
|
40
|
+
</SelectTrigger>
|
|
41
|
+
<SelectContent>
|
|
42
|
+
{formats.map((format) => (
|
|
43
|
+
<SelectItem key={format} value={format} className="!text-xs font-normal text-gray-700">
|
|
44
|
+
{format.toUpperCase()}
|
|
45
|
+
</SelectItem>
|
|
46
|
+
))}
|
|
47
|
+
</SelectContent>
|
|
48
|
+
</Select>
|
|
49
|
+
);
|
|
50
|
+
};
|
package/template/+nextjs+azure-web-pubsub/components/ui/color-picker/selector/color-picker-hue.tsx
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import { Range, Root, Thumb, Track } from "@radix-ui/react-slider";
|
|
6
|
+
import {
|
|
7
|
+
type HTMLAttributes,
|
|
8
|
+
useCallback,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from "react";
|
|
12
|
+
import { useColorPicker } from "../context/color-picker-context";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export type ColorPickerHueProps = HTMLAttributes<HTMLDivElement>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* ColorPickerHue component
|
|
19
|
+
* A slider for selecting the hue value
|
|
20
|
+
*/
|
|
21
|
+
export const ColorPickerHue = ({
|
|
22
|
+
className,
|
|
23
|
+
dir,
|
|
24
|
+
...props
|
|
25
|
+
}: ColorPickerHueProps) => {
|
|
26
|
+
const { defaultValue, ...restProps } = props;
|
|
27
|
+
const { color, setColor } = useColorPicker();
|
|
28
|
+
const [hueValue, setHueValue] = useState(color.hue());
|
|
29
|
+
const lastHue = useRef(color.hue());
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
const onValueChange = useCallback(
|
|
33
|
+
([hue]: number[]) => {
|
|
34
|
+
setHueValue(hue);
|
|
35
|
+
},
|
|
36
|
+
[setHueValue]
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const onValueCommit = useCallback(
|
|
40
|
+
([hue]: number[]) => {
|
|
41
|
+
lastHue.current = hue;
|
|
42
|
+
setColor(color.hue(hue));
|
|
43
|
+
},
|
|
44
|
+
[color, setColor]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Root
|
|
49
|
+
value={[hueValue]}
|
|
50
|
+
max={360}
|
|
51
|
+
step={1}
|
|
52
|
+
className={cn(
|
|
53
|
+
"relative flex h-5 w-full touch-none items-center transition-opacity duration-200",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
onValueChange={onValueChange}
|
|
57
|
+
onValueCommit={onValueCommit}
|
|
58
|
+
aria-label="Hue"
|
|
59
|
+
{...restProps}
|
|
60
|
+
>
|
|
61
|
+
<Track className="relative my-0.5 h-3 w-full grow overflow-hidden rounded-full bg-[linear-gradient(90deg,#FF0000,#FFFF00,#00FF00,#00FFFF,#0000FF,#FF00FF,#FF0000)]">
|
|
62
|
+
<Range className="absolute h-full" />
|
|
63
|
+
</Track>
|
|
64
|
+
<Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:h-5 hover:w-5" />
|
|
65
|
+
</Root>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HTMLAttributes,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react";
|
|
9
|
+
import { useColorPicker } from "../context/color-picker-context";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import Color from "color";
|
|
12
|
+
|
|
13
|
+
export type ColorPickerSaturationProps = HTMLAttributes<HTMLDivElement>;
|
|
14
|
+
|
|
15
|
+
export const ColorPickerSaturation = ({
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: ColorPickerSaturationProps) => {
|
|
19
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
20
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
21
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
22
|
+
const { color, setColor } = useColorPicker();
|
|
23
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
24
|
+
const hue = useMemo(() => color.hue(), [color]);
|
|
25
|
+
|
|
26
|
+
// Function to render the gradient on the hidden canvas
|
|
27
|
+
const renderGradient = useCallback(() => {
|
|
28
|
+
if (canvasRef.current) {
|
|
29
|
+
const canvas = canvasRef.current;
|
|
30
|
+
const ctx = canvas.getContext("2d");
|
|
31
|
+
if (ctx && containerRef.current) {
|
|
32
|
+
const { width, height } = containerRef.current.getBoundingClientRect();
|
|
33
|
+
canvas.width = width;
|
|
34
|
+
canvas.height = height;
|
|
35
|
+
|
|
36
|
+
const gradX = ctx.createLinearGradient(0, 0, width, 0);
|
|
37
|
+
gradX.addColorStop(0.1, "rgb(255,255,255)");
|
|
38
|
+
gradX.addColorStop(0.9, `hsl(${hue}, 100%, 50%)`);
|
|
39
|
+
ctx.fillStyle = gradX;
|
|
40
|
+
ctx.fillRect(0, 0, width, height);
|
|
41
|
+
|
|
42
|
+
const gradY = ctx.createLinearGradient(0, 0, 0, height);
|
|
43
|
+
gradY.addColorStop(0.1, "rgba(0,0,0,0)");
|
|
44
|
+
gradY.addColorStop(0.9, "rgba(0,0,0,1)");
|
|
45
|
+
ctx.fillStyle = gradY;
|
|
46
|
+
ctx.fillRect(0, 0, width, height);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}, [hue]);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
renderGradient();
|
|
53
|
+
}, [renderGradient]);
|
|
54
|
+
|
|
55
|
+
const handlePointerMove = useCallback(
|
|
56
|
+
(event: PointerEvent) => {
|
|
57
|
+
if (!isDragging || !containerRef.current || !canvasRef.current) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
62
|
+
const x = Math.max(
|
|
63
|
+
0,
|
|
64
|
+
Math.min(rect.width - 1, event.clientX - rect.left)
|
|
65
|
+
);
|
|
66
|
+
const y = Math.max(
|
|
67
|
+
0,
|
|
68
|
+
Math.min(rect.height - 1, event.clientY - rect.top)
|
|
69
|
+
);
|
|
70
|
+
setPosition({ x: x / rect.width, y: y / rect.height });
|
|
71
|
+
|
|
72
|
+
const ctx = canvasRef.current.getContext("2d");
|
|
73
|
+
if (ctx) {
|
|
74
|
+
const pixel = ctx.getImageData(x, y, 1, 1).data;
|
|
75
|
+
|
|
76
|
+
const [r, g, b] = pixel;
|
|
77
|
+
|
|
78
|
+
let newColor = Color.rgb(r, g, b);
|
|
79
|
+
|
|
80
|
+
const newHsv = newColor.hsv().object();
|
|
81
|
+
const oldHsv = color.hsv().object();
|
|
82
|
+
|
|
83
|
+
if (newHsv.s === 0) {
|
|
84
|
+
newHsv.h = oldHsv.h;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
newColor = Color.hsv(newHsv.h, newHsv.s, newHsv.v).alpha(color.alpha());
|
|
88
|
+
setColor(newColor);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
[color, isDragging, setColor]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (isDragging) {
|
|
96
|
+
const onPointerUp = () => setIsDragging(false);
|
|
97
|
+
window.addEventListener("pointermove", handlePointerMove);
|
|
98
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
99
|
+
return () => {
|
|
100
|
+
window.removeEventListener("pointermove", handlePointerMove);
|
|
101
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}, [isDragging, handlePointerMove]);
|
|
105
|
+
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (!isDragging && containerRef.current) {
|
|
108
|
+
const { s, v } = color.hsv().object();
|
|
109
|
+
const newX = s / 100;
|
|
110
|
+
const newY = (100 - v) / 100;
|
|
111
|
+
setPosition({ x: newX, y: newY });
|
|
112
|
+
}
|
|
113
|
+
}, [color, isDragging]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<>
|
|
117
|
+
<div
|
|
118
|
+
ref={containerRef}
|
|
119
|
+
className={cn(
|
|
120
|
+
"relative aspect-[4/3] w-full cursor-crosshair rounded-none",
|
|
121
|
+
className
|
|
122
|
+
)}
|
|
123
|
+
style={{
|
|
124
|
+
background: `linear-gradient(0deg, rgb(0,0,0), transparent),linear-gradient(90deg, rgb(255,255,255), hsl(${hue},100%,50%))`,
|
|
125
|
+
}}
|
|
126
|
+
onPointerDown={(e) => {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
setIsDragging(true);
|
|
129
|
+
handlePointerMove(e.nativeEvent);
|
|
130
|
+
}}
|
|
131
|
+
{...props}
|
|
132
|
+
>
|
|
133
|
+
<div
|
|
134
|
+
className="-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute h-4 w-4 rounded-full border-2 border-white"
|
|
135
|
+
style={{
|
|
136
|
+
left: `${position.x * 100}%`,
|
|
137
|
+
top: `${position.y * 100}%`,
|
|
138
|
+
boxShadow: "0 0 0 1px rgba(0,0,0,0.5)",
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
<canvas ref={canvasRef} style={{ display: "none" }} />
|
|
143
|
+
</>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Input } from "@/components/ui/input";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import {
|
|
4
|
+
type ComponentProps,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { useColorPicker } from "../context/color-picker-context";
|
|
9
|
+
import Color, { ColorInstance } from "color";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* PercentageInput component
|
|
13
|
+
* An input field for percentage values with validation
|
|
14
|
+
*/
|
|
15
|
+
const PercentageInput = ({
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: ComponentProps<typeof Input>) => {
|
|
19
|
+
const { color, setColor } = useColorPicker();
|
|
20
|
+
const [actualValue, setActualValue] = useState<ColorInstance>(color);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setActualValue(color);
|
|
24
|
+
}, [color]);
|
|
25
|
+
|
|
26
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
27
|
+
try {
|
|
28
|
+
const newColor = Color(color.alpha(Number(e.target.value) / 100));
|
|
29
|
+
setColor(newColor);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Invalid color value", error);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="relative">
|
|
37
|
+
<Input
|
|
38
|
+
type="number"
|
|
39
|
+
max={100}
|
|
40
|
+
min={0}
|
|
41
|
+
step={1}
|
|
42
|
+
inputMode="numeric"
|
|
43
|
+
pattern="[0-9]*"
|
|
44
|
+
value={Math.round(actualValue.alpha() * 100)}
|
|
45
|
+
onChange={handleInputChange}
|
|
46
|
+
aria-label="Opacity percentage"
|
|
47
|
+
{...props}
|
|
48
|
+
className={cn(
|
|
49
|
+
"h-8 w-[4.7rem] rounded-none !text-xs font-normal text-gray-700 text-left focus:outline-none bg-transparent uppercase",
|
|
50
|
+
className
|
|
51
|
+
)}
|
|
52
|
+
/>
|
|
53
|
+
<span className="-translate-y-1/2 absolute -translate-x-5 top-1/2 right-2 text-muted-foreground text-xs pointer-events-none">
|
|
54
|
+
%
|
|
55
|
+
</span>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default PercentageInput;
|