react-magic-portal 1.2.1 → 1.3.1
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/CHANGELOG.md +14 -0
- package/README.md +7 -7
- package/__tests__/src/magic-portal.test.tsx +46 -90
- package/package.json +1 -1
- package/packages/component/dist/index.d.ts +2 -2
- package/packages/component/dist/index.d.ts.map +1 -1
- package/packages/component/dist/index.js +1 -1
- package/packages/component/dist/index.js.map +1 -1
- package/packages/component/eslint.config.ts +2 -0
- package/packages/component/src/index.ts +30 -15
- package/packages/example/dist/assets/{index-jJ0JbhKk.js → index-D7od5Grv.js} +12 -12
- package/packages/example/dist/assets/{index-CDQ6J_Ti.css → index-cHGDwajU.css} +1 -1
- package/packages/example/dist/index.html +2 -2
- package/packages/example/src/App.css +2 -2
- package/packages/example/src/App.tsx +8 -8
- package/packages/example/src/components/portal-content.tsx +4 -4
- package/packages/example/src/index.css +42 -42
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}body{margin:0;padding:0;min-height:100vh;background:#fff;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;display:flex;justify-content:center;align-items:flex-start}.app{max-width:900px;padding:3rem;font-family:inherit}h1{color:#2d3748;text-align:center;font-size:2.5rem;font-weight:700;background:linear-gradient(135deg,#667eea,#764ba2);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}h2{color:#4a5568;margin-top:2rem;margin-bottom:1rem;font-size:1.5rem;font-weight:600}.controls{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1.5rem;margin-bottom:3rem;justify-content:center}.controls button{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;padding:1rem 2rem;border-radius:.75rem;cursor:pointer;font-size:1rem;font-weight:500;box-shadow:0 4px 15px #667eea4d;transition:all .3s ease;position:relative;overflow:hidden}.controls button:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.2),transparent);transition:left .5s}.controls button:hover:before{left:100%}.controls button:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 25px #667eea66}.controls button:disabled{background:linear-gradient(135deg,#a0aec0,#718096);cursor:not-allowed;transform:none;box-shadow:0 2px 8px #0000001a}.dynamic-anchor{background:linear-gradient(145deg,#fff,#f7fafc);border:2px solid #667eea;border-radius:.75rem;padding:1.5rem;text-align:center;font-weight:600;color:#2d3748;position:relative;margin:1rem 0;opacity:0;animation:anchorAppear .4s ease-out .2s forwards;box-shadow:0 4px 15px #667eea1a;transition:all .3s ease}.dynamic-anchor:hover{transform:translateY(-2px);box-shadow:0 8px 25px #667eea33}.portal-content{padding:1rem 1.5rem;margin:.75rem 0;border-radius:.75rem;font-size:.95rem;font-weight:500;border:2px solid;position:relative;opacity:0;animation:portalAppear .4s ease-out 1s forwards;box-shadow:0 4px 15px #0000001a;transition:all .3s ease}.portal-content:hover{transform:translateY(-2px);box-shadow:0 8px 25px #00000026}.portal-content.
|
|
1
|
+
:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}body{margin:0;padding:0;min-height:100vh;background:#fff;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;display:flex;justify-content:center;align-items:flex-start}.app{max-width:900px;padding:3rem;font-family:inherit}h1{color:#2d3748;text-align:center;font-size:2.5rem;font-weight:700;background:linear-gradient(135deg,#667eea,#764ba2);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}h2{color:#4a5568;margin-top:2rem;margin-bottom:1rem;font-size:1.5rem;font-weight:600}.controls{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1.5rem;margin-bottom:3rem;justify-content:center}.controls button{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;padding:1rem 2rem;border-radius:.75rem;cursor:pointer;font-size:1rem;font-weight:500;box-shadow:0 4px 15px #667eea4d;transition:all .3s ease;position:relative;overflow:hidden}.controls button:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.2),transparent);transition:left .5s}.controls button:hover:before{left:100%}.controls button:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 25px #667eea66}.controls button:disabled{background:linear-gradient(135deg,#a0aec0,#718096);cursor:not-allowed;transform:none;box-shadow:0 2px 8px #0000001a}.dynamic-anchor{background:linear-gradient(145deg,#fff,#f7fafc);border:2px solid #667eea;border-radius:.75rem;padding:1.5rem;text-align:center;font-weight:600;color:#2d3748;position:relative;margin:1rem 0;opacity:0;animation:anchorAppear .4s ease-out .2s forwards;box-shadow:0 4px 15px #667eea1a;transition:all .3s ease}.dynamic-anchor:hover{transform:translateY(-2px);box-shadow:0 8px 25px #667eea33}.portal-content{padding:1rem 1.5rem;margin:.75rem 0;border-radius:.75rem;font-size:.95rem;font-weight:500;border:2px solid;position:relative;opacity:0;animation:portalAppear .4s ease-out 1s forwards;box-shadow:0 4px 15px #0000001a;transition:all .3s ease}.portal-content:hover{transform:translateY(-2px);box-shadow:0 8px 25px #00000026}.portal-content.last{background:linear-gradient(135deg,#d4edda,#c3e6cb);border-color:#28a745;color:#155724;animation:portalAppearFromRight .4s ease-out 1s forwards}.portal-content.first{background:linear-gradient(135deg,#cce5ff,#b8daff);border-color:#007bff;color:#004085;animation:portalAppearFromLeft .4s ease-out 1s forwards}.portal-content.before{background:linear-gradient(135deg,#fff3cd,#ffeaa7);border-color:#ffc107;color:#856404;animation:portalAppearFromLeft .4s ease-out 1s forwards}.portal-content.after{background:linear-gradient(135deg,#f8d7da,#f1b0b7);border-color:#dc3545;color:#721c24;animation:portalAppearFromRight .4s ease-out 1s forwards}.portal-content.dynamic{background-color:#ffeaa7;border-color:#fdcb6e;color:#6c5ce7;font-weight:700}.portal-preview{min-height:500px;display:flex;flex-direction:column;align-items:center;justify-content:center;border:3px dashed #667eea;border-radius:1rem;background:linear-gradient(145deg,#f7fafc,#edf2f7);padding:1.5rem;position:relative;overflow:hidden}.portal-preview:after{content:"";position:absolute;inset:0;background:linear-gradient(45deg,transparent 30%,rgba(102,126,234,.03) 50%,transparent 70%);pointer-events:none}.portal-preview:empty:before{content:"📍 Target anchor is hidden. Portal content will not appear.";opacity:.8;font-weight:500;color:#4a5568;text-align:center;font-size:1.1rem}.instructions{margin-top:2rem;padding:2.5rem;background:linear-gradient(145deg,#f7fafc,#edf2f7);border-radius:1rem;border:1px solid rgba(102,126,234,.2);box-shadow:0 4px 15px #0000000d}.instructions ul{margin:1rem 0;padding-left:2rem}.instructions li{margin:.5rem 0;line-height:1.5}.instructions strong{color:#646cff;font-family:monospace;background-color:#f1f3f4;padding:.125rem .25rem;border-radius:.25rem}.instructions p{margin-top:1rem;color:#6c757d;font-style:italic}@media (max-width: 768px){.app{padding:1rem}.controls{flex-direction:column}.controls button{width:100%}}@keyframes anchorAppear{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes portalAppear{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes portalAppearFromLeft{0%{opacity:0;transform:translate(-30px)}to{opacity:1;transform:translate(0)}}@keyframes portalAppearFromRight{0%{opacity:0;transform:translate(30px)}to{opacity:1;transform:translate(0)}}.portal-content:before{content:"Portal";position:absolute;top:-.75rem;left:1rem;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;padding:.25rem .75rem;font-size:.75rem;font-weight:600;border-radius:1rem;z-index:1;box-shadow:0 2px 8px #667eea4d}.dynamic-anchor:before{content:"Anchor";position:absolute;top:-.75rem;left:1rem;background:linear-gradient(135deg,#48bb78,#38a169);color:#fff;padding:.25rem .75rem;font-size:.75rem;font-weight:600;border-radius:1rem;z-index:1;box-shadow:0 2px 8px #48bb784d}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>React Magic Portal Example</title>
|
|
8
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-D7od5Grv.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-cHGDwajU.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -125,14 +125,14 @@ h2 {
|
|
|
125
125
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
.portal-content.
|
|
128
|
+
.portal-content.last {
|
|
129
129
|
background: linear-gradient(135deg, #d4edda, #c3e6cb);
|
|
130
130
|
border-color: #28a745;
|
|
131
131
|
color: #155724;
|
|
132
132
|
animation: portalAppearFromRight 0.4s ease-out 1s forwards;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
.portal-content.
|
|
135
|
+
.portal-content.first {
|
|
136
136
|
background: linear-gradient(135deg, #cce5ff, #b8daff);
|
|
137
137
|
border-color: #007bff;
|
|
138
138
|
color: #004085;
|
|
@@ -33,16 +33,16 @@ function App() {
|
|
|
33
33
|
</div>
|
|
34
34
|
|
|
35
35
|
{/* Target anchor examples */}
|
|
36
|
-
<MagicPortal anchor="#dynamic-anchor" position="
|
|
37
|
-
<PortalContent position="
|
|
36
|
+
<MagicPortal anchor="#dynamic-anchor" position="before" onMount={handleMount} onUnmount={handleUnmount}>
|
|
37
|
+
<PortalContent position="before" />
|
|
38
38
|
</MagicPortal>
|
|
39
39
|
|
|
40
|
-
<MagicPortal anchor="#dynamic-anchor" position="
|
|
41
|
-
<PortalContent position="
|
|
40
|
+
<MagicPortal anchor="#dynamic-anchor" position="first" onMount={handleMount} onUnmount={handleUnmount}>
|
|
41
|
+
<PortalContent position="first" />
|
|
42
42
|
</MagicPortal>
|
|
43
43
|
|
|
44
|
-
<MagicPortal anchor="#dynamic-anchor" position="
|
|
45
|
-
<PortalContent position="
|
|
44
|
+
<MagicPortal anchor="#dynamic-anchor" position="last" onMount={handleMount} onUnmount={handleUnmount}>
|
|
45
|
+
<PortalContent position="last" />
|
|
46
46
|
</MagicPortal>
|
|
47
47
|
|
|
48
48
|
<MagicPortal anchor="#dynamic-anchor" position="after" onMount={handleMount} onUnmount={handleUnmount}>
|
|
@@ -53,10 +53,10 @@ function App() {
|
|
|
53
53
|
<div className="instructions">
|
|
54
54
|
<ul>
|
|
55
55
|
<li>
|
|
56
|
-
<strong>
|
|
56
|
+
<strong>last</strong>: Adds content inside the anchor element at the end
|
|
57
57
|
</li>
|
|
58
58
|
<li>
|
|
59
|
-
<strong>
|
|
59
|
+
<strong>first</strong>: Adds content inside the anchor element at the beginning
|
|
60
60
|
</li>
|
|
61
61
|
<li>
|
|
62
62
|
<strong>before</strong>: Adds content as a sibling before the target anchor
|
|
@@ -15,10 +15,10 @@ function PortalContent({ position, ref }: PortalContentProps) {
|
|
|
15
15
|
|
|
16
16
|
const getContentText = () => {
|
|
17
17
|
switch (position) {
|
|
18
|
-
case '
|
|
19
|
-
return '
|
|
20
|
-
case '
|
|
21
|
-
return '
|
|
18
|
+
case 'last':
|
|
19
|
+
return 'Last Content (inside target, at end)'
|
|
20
|
+
case 'first':
|
|
21
|
+
return 'First Content (inside target, at beginning)'
|
|
22
22
|
case 'before':
|
|
23
23
|
return 'Before Content (sibling, before target)'
|
|
24
24
|
case 'after':
|
|
@@ -1,68 +1,68 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
color: rgba(255, 255, 255, 0.87);
|
|
8
|
+
background-color: #242424;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
a {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
font-weight: 500;
|
|
18
|
+
color: #646cff;
|
|
19
|
+
text-decoration: inherit;
|
|
20
20
|
}
|
|
21
21
|
a:hover {
|
|
22
|
-
|
|
22
|
+
color: #535bf2;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
body {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
margin: 0;
|
|
27
|
+
display: flex;
|
|
28
|
+
place-items: center;
|
|
29
|
+
min-width: 320px;
|
|
30
|
+
min-height: 100vh;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
h1 {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
font-size: 3.2em;
|
|
35
|
+
line-height: 1.1;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
button {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
border-radius: 8px;
|
|
40
|
+
border: 1px solid transparent;
|
|
41
|
+
padding: 0.6em 1.2em;
|
|
42
|
+
font-size: 1em;
|
|
43
|
+
font-weight: 500;
|
|
44
|
+
font-family: inherit;
|
|
45
|
+
background-color: #1a1a1a;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
transition: border-color 0.25s;
|
|
48
48
|
}
|
|
49
49
|
button:hover {
|
|
50
|
-
|
|
50
|
+
border-color: #646cff;
|
|
51
51
|
}
|
|
52
52
|
button:focus,
|
|
53
53
|
button:focus-visible {
|
|
54
|
-
|
|
54
|
+
outline: 4px auto -webkit-focus-ring-color;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
@media (prefers-color-scheme: light) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
:root {
|
|
59
|
+
color: #213547;
|
|
60
|
+
background-color: #ffffff;
|
|
61
|
+
}
|
|
62
|
+
a:hover {
|
|
63
|
+
color: #747bff;
|
|
64
|
+
}
|
|
65
|
+
button {
|
|
66
|
+
background-color: #f9f9f9;
|
|
67
|
+
}
|
|
68
68
|
}
|