-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(Modal): add forceMount prop to allow modal to render in SSR #5659
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
Changes from all commits
f33661b
6e48bba
d107652
72f0b20
5ad0e02
ba23ae1
b00b849
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,18 +1,61 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { inject, computed } from 'vue' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { Ref, InjectionKey } from 'vue' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { DialogPortalProps } from 'reka-ui' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const portalTargetInjectionKey: InjectionKey<Ref<boolean | string | HTMLElement>> = Symbol('nuxt-ui.portal-target') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function usePortal(portal: Ref<boolean | string | HTMLElement | undefined>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type PortalProps = boolean | string | HTMLElement | DialogPortalProps | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function isDialogPortalProps(p: unknown): p is DialogPortalProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return typeof p === 'object' && p !== null && ('to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+11
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against HTMLElements in Line 9-11: any object with π οΈ Suggested fixfunction isDialogPortalProps(p: unknown): p is DialogPortalProps {
- return typeof p === 'object' && p !== null && ('to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p)
+ if (typeof p !== 'object' || p === null) return false
+ // Exclude DOM nodes/HTMLElements from being treated as portal config objects
+ if ('nodeType' in (p as any)) return false
+ return 'to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p
}π Committable suggestion
Suggested change
π€ Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function usePortal(portal: Ref<PortalProps | undefined>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const globalPortal = inject(portalTargetInjectionKey, undefined) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const value = computed(() => portal.value === true ? globalPortal?.value : portal.value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const value = computed((): boolean | string | HTMLElement | undefined => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const p = portal.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (p === true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return globalPortal?.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isDialogPortalProps(p)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return p.to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return p | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const disabled = computed(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const p = portal.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isDialogPortalProps(p) && p.disabled !== undefined) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return p.disabled | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return typeof value.value === 'boolean' ? !value.value : false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const to = computed(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isDialogPortalProps(portal.value) && (typeof portal.value?.to === 'boolean' || typeof portal.value?.to === 'undefined')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 'body' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof value.value === 'boolean') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 'body' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const disabled = computed(() => typeof value.value === 'boolean' ? !value.value : false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const to = computed(() => typeof value.value === 'boolean' ? 'body' : value.value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return value.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const forceMount = computed(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const p = portal.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return isDialogPortalProps(p) && p.forceMount === true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return computed(() => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| to: to.value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled: disabled.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled: disabled.value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| forceMount: forceMount.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
60
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π§© Analysis chainπ Script executed: find . -type f -name "usePortal.ts" -o -name "*Portal*" | head -20Repository: nuxt/ui Length of output: 89 π Script executed: cat -n ./src/runtime/composables/usePortal.tsRepository: nuxt/ui Length of output: 2141 π Script executed: rg "usePortal" -A 5 -B 2 --type ts | head -50Repository: nuxt/ui Length of output: 588 π Script executed: rg "from.*usePortal|import.*usePortal" --type ts -A 3Repository: nuxt/ui Length of output: 33 π Script executed: rg "usePortal" --type ts -B 2 -A 5Repository: nuxt/ui Length of output: 588 π Script executed: fd -e ts -e tsx | xargs grep -l "usePortal" 2>/dev/null | head -10Repository: nuxt/ui Length of output: 87 π Script executed: fd -type f -name "*Dialog*" -o -name "*dialog*" | grep -E "\.(ts|tsx|vue)$"Repository: nuxt/ui Length of output: 221 π Script executed: fd "Dialog|dialog" -e ts -e tsx -e vueRepository: nuxt/ui Length of output: 33 π Script executed: ls -la src/runtime/ && echo "---" && find src/runtime -maxdepth 2 -type dRepository: nuxt/ui Length of output: 1121 π Script executed: ls src/runtime/components/ && echo "---" && find src/runtime/components -maxdepth 1 -type dRepository: nuxt/ui Length of output: 1925 π Script executed: cat -n src/runtime/components/Modal.vue | head -100Repository: nuxt/ui Length of output: 3880 π Script executed: cat -n src/runtime/components/Modal.vue | sed -n '100,200p'Repository: nuxt/ui Length of output: 4653 π Script executed: cat -n src/runtime/components/Modal.vue | sed -n '200,250p'Repository: nuxt/ui Length of output: 1270 π Script executed: grep -r "DialogPortal" --include="*.ts" --include="*.tsx" -A 5 -B 2 | head -50Repository: nuxt/ui Length of output: 3151 π Script executed: cat package.json | grep -A 20 "dependencies"Repository: nuxt/ui Length of output: 835 π Script executed: cat package.json | grep "reka-ui"Repository: nuxt/ui Length of output: 74 Forward Line 10 of the guard confirms Suggested fix+ const defer = computed(() => {
+ const p = portal.value
+ return isDialogPortalProps(p) ? p.defer : undefined
+ })
return computed(() => ({
to: to.value,
disabled: disabled.value,
- forceMount: forceMount.value
+ forceMount: forceMount.value,
+ defer: defer.value
}))π Committable suggestion
Suggested change
π€ Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Fix markdownlint MD007/MD018 in the Force Mount example.
Line 371 (MD007) and Line 389 (MD018) are flagged in this new block. Consider scoping a markdownlint disable to this component-code snippet.
π οΈ Suggested fix
π§° Tools
πͺ markdownlint-cli2 (0.20.0)
371-371: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
389-389: No space after hash on atx style heading
(MD018, no-missing-space-atx)
π€ Prompt for AI Agents