-
Notifications
You must be signed in to change notification settings - Fork 10
docs: add responsive toggle button and update preview to iframe #258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MaxLee-dev
wants to merge
12
commits into
main
Choose a base branch
from
add-block-responsive
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
fe6cc30
Refactor code structure for improved readability and maintainability
MaxLee-dev 7001ece
feat: add responsive toggle to demo components and update form widths…
MaxLee-dev 7d02113
feat: add IframePreview and ResizablePreview components for enhanced …
MaxLee-dev 1d3ba3e
feat: refactor demo components and add device type constants for impr…
MaxLee-dev 9dc0e9d
refactor: refactor button-toggle-group
MaxLee-dev 33cfac4
fix: remove tabIndex prop from ToggleItem component for improved acce…
MaxLee-dev c3da80a
feat: update CodeBlock export and refactor demo components for improv…
MaxLee-dev b0a104b
refactor: remove unused className props and simplify component struct…
MaxLee-dev 3bc9f29
Merge branch 'main' of https://github.com/goorm-dev/vapor-ui into add…
MaxLee-dev 93f6295
fix(preview): update error text color and validate component path
MaxLee-dev bd324c4
fix(preview): improve error handling for dynamic component loading
MaxLee-dev 0835bf5
fix(demo): clean up comments and improve header styling
MaxLee-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| 'use client'; | ||
|
|
||
| import * as React from 'react'; | ||
| import { Suspense } from 'react'; | ||
|
|
||
| import { Text } from '@vapor-ui/core'; | ||
|
|
||
| interface PreviewPageProps { | ||
| searchParams: Promise<{ | ||
| path?: string; | ||
| }>; | ||
| } | ||
|
|
||
| const ComponentError = ({ | ||
| componentPath, | ||
| error, | ||
| }: { | ||
| componentPath: string | undefined; | ||
| error: string; | ||
| }) => { | ||
| return ( | ||
| <div className="p-5 text-center text-v-danger"> | ||
| <Text typography="heading1" foreground="danger-100" render={<h1 />} className="mb-4"> | ||
| Component not found | ||
| </Text> | ||
| <Text typography="body2" foreground="danger-100"> | ||
| {error} | ||
| </Text> | ||
| {componentPath && ( | ||
| <Text typography="body2" foreground="danger-100"> | ||
| {componentPath} | ||
| </Text> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| function ComponentLoading() { | ||
| return ( | ||
| <div className="p-5 text-center text-gray-500"> | ||
| <div className="animate-spin w-6 h-6 border-2 border-v-gray-300 border-t-v-blue-500 rounded-full mx-auto mb-2" /> | ||
| <p>Loading component...</p> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| function useDynamicComponent(componentPath?: string) { | ||
| const [component, setComponent] = React.useState<React.ComponentType | null>(null); | ||
| const [error, setError] = React.useState<string | null>(null); | ||
| const [isLoading, setIsLoading] = React.useState(true); | ||
|
|
||
| React.useEffect(() => { | ||
| if (!componentPath) { | ||
| setError('No component path provided'); | ||
| setIsLoading(false); | ||
| return; | ||
| } | ||
|
|
||
| const loadComponent = async () => { | ||
| try { | ||
| setIsLoading(true); | ||
| setError(null); | ||
|
|
||
| const componentModule = await import( | ||
| `~/components/demo/examples/${componentPath}.tsx` | ||
| ); | ||
|
|
||
| if (componentModule.default) { | ||
| setComponent(() => componentModule.default); | ||
| } else { | ||
| setError(`Component "${componentPath}" does not have a default export`); | ||
| } | ||
| } catch (err) { | ||
| setError(`Could not load component: ${componentPath}. ${(err as Error).message}`); | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| loadComponent(); | ||
| }, [componentPath]); | ||
|
|
||
| return { component, error, isLoading }; | ||
| } | ||
|
|
||
| function DynamicComponent({ componentPath }: { componentPath?: string }) { | ||
| const { component: Component, error, isLoading } = useDynamicComponent(componentPath); | ||
|
|
||
| if (error) { | ||
| return <ComponentError componentPath={componentPath} error={error} />; | ||
| } | ||
|
|
||
| if (isLoading || !Component) { | ||
| return <ComponentLoading />; | ||
| } | ||
|
|
||
| return <Component />; | ||
| } | ||
| function isValidComponentPath(path?: string): boolean { | ||
| if (!path) return false; | ||
|
|
||
| const pathTraversalPattern = /\.\./; | ||
| if (pathTraversalPattern.test(path)) return false; | ||
|
|
||
| const validPathPattern = /^[a-zA-Z0-9/_-]+$/; | ||
| return validPathPattern.test(path); | ||
| } | ||
|
|
||
| export default function Page({ searchParams }: PreviewPageProps) { | ||
| const resolvedSearchParams = React.use(searchParams); | ||
| const componentPath = resolvedSearchParams.path; | ||
|
|
||
| if (!isValidComponentPath(componentPath)) { | ||
| return <ComponentError componentPath={componentPath} error="Invalid component path" />; | ||
| } | ||
|
|
||
| return ( | ||
| <Suspense fallback={<div>Loading...</div>}> | ||
| <DynamicComponent componentPath={componentPath} /> | ||
| </Suspense> | ||
| ); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
보안 취약점:
componentPath를 동적import에 사용하기 전에 유효성 검사를 추가해야 합니다. 현재 코드는searchParams에서 받은 경로를 그대로 사용하므로 경로 조작(Path Traversal) 공격에 취약할 수 있습니다.componentPath에../와 같은 문자가 포함되어 있는지 확인하고, 허용된 문자(예: 영문, 숫자,-,/,_)만으로 구성되었는지 검증하는 로직을 이 부분에 추가하는 것을 권장합니다.