@the_disreputable_doggo/password-gate 1.0.2 → 1.1.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/README.md +24 -4
- package/dist/index.d.mts +16 -7
- package/dist/index.d.ts +16 -7
- package/dist/index.js +31 -5
- package/dist/index.mjs +31 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,26 +10,46 @@ npm install @the_disreputable_doggo/password-gate
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
+
### Simple Message (Recommended)
|
|
14
|
+
|
|
13
15
|
```tsx
|
|
14
16
|
import PasswordGate from '@the_disreputable_doggo/password-gate';
|
|
15
17
|
|
|
16
18
|
function App() {
|
|
17
19
|
return (
|
|
18
20
|
<PasswordGate
|
|
19
|
-
title="
|
|
21
|
+
title="Site Launch"
|
|
20
22
|
subtitle="Enter your password"
|
|
21
|
-
|
|
22
|
-
underConstruction="Building in progress"
|
|
23
|
+
message="Coming soon!"
|
|
23
24
|
placeholder="Password"
|
|
24
25
|
buttonText="Enter"
|
|
25
26
|
enabled={true}
|
|
26
27
|
password="secretpassword"
|
|
27
|
-
releaseDate="2027-01-01"
|
|
28
28
|
/>
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
### Automatic Fallback
|
|
34
|
+
|
|
35
|
+
When omitting both `message` and `releaseDate`, it automatically displays `"Coming soon!"` (or `"¡Próximamente!"` if Spanish is detected via `lang` prop or `document.documentElement.lang`).
|
|
36
|
+
|
|
37
|
+
### Date-Based Launch (Legacy)
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<PasswordGate
|
|
41
|
+
title="Coming Soon"
|
|
42
|
+
subtitle="Enter your password"
|
|
43
|
+
datePrefix="Expected"
|
|
44
|
+
releaseDate="2027-01-01"
|
|
45
|
+
underConstruction="Building in progress"
|
|
46
|
+
placeholder="Password"
|
|
47
|
+
buttonText="Enter"
|
|
48
|
+
enabled={true}
|
|
49
|
+
password="secretpassword"
|
|
50
|
+
/>
|
|
51
|
+
```
|
|
52
|
+
|
|
33
53
|
## Features
|
|
34
54
|
- Fully customizable text props for internationalization.
|
|
35
55
|
- Accepts configuration variables dynamically.
|
package/dist/index.d.mts
CHANGED
|
@@ -3,13 +3,15 @@ import React from 'react';
|
|
|
3
3
|
interface PasswordGateProps {
|
|
4
4
|
title: string;
|
|
5
5
|
subtitle: string;
|
|
6
|
-
datePrefix: string;
|
|
7
|
-
underConstruction: string;
|
|
8
6
|
placeholder: string;
|
|
9
7
|
buttonText: string;
|
|
8
|
+
message?: string;
|
|
9
|
+
datePrefix?: string;
|
|
10
|
+
underConstruction?: string;
|
|
11
|
+
releaseDate?: string;
|
|
10
12
|
enabled?: boolean;
|
|
11
13
|
password?: string;
|
|
12
|
-
|
|
14
|
+
lang?: string;
|
|
13
15
|
onUnlock?: () => void;
|
|
14
16
|
showPasswordLabel?: string;
|
|
15
17
|
hidePasswordLabel?: string;
|
|
@@ -19,24 +21,31 @@ interface PasswordGateProps {
|
|
|
19
21
|
* PasswordGate
|
|
20
22
|
*
|
|
21
23
|
* Why:
|
|
22
|
-
* Full-screen modal overlay that restricts site access during
|
|
24
|
+
* Full-screen modal overlay that restricts site access during pre-launch or staging phases.
|
|
23
25
|
* Validates user input against a configured password, persists unlock state in localStorage, and
|
|
24
26
|
* removes the locking CSS class from the document root.
|
|
25
27
|
*
|
|
26
28
|
* Inputs:
|
|
27
|
-
* - props: PasswordGateProps including localized copy strings,
|
|
29
|
+
* - props: PasswordGateProps including localized copy strings (title, subtitle, message, placeholder, buttonText),
|
|
30
|
+
* legacy date props (datePrefix, releaseDate, underConstruction), configuration (enabled, password, unlockBodyClass, lang),
|
|
31
|
+
* and callback (onUnlock).
|
|
28
32
|
*
|
|
29
33
|
* Output:
|
|
30
34
|
* - JSX.Element | null (renders null if disabled or already unlocked).
|
|
31
35
|
*
|
|
32
36
|
* Edge cases & handling:
|
|
37
|
+
* - Fallback copy: If `message` is passed, it takes precedence. If `releaseDate` is passed, it preserves
|
|
38
|
+
* legacy `{datePrefix} {releaseDate} {underConstruction}` display. If neither is passed, it detects
|
|
39
|
+
* locale (via `lang` prop or document.documentElement.lang) and renders "¡Próximamente!" for Spanish
|
|
40
|
+
* or "Coming soon!" for English.
|
|
33
41
|
* - Accessibility landmark: Wraps the gate in <main id="main-content" role="main"> so automated audits
|
|
34
42
|
* and screen readers detect a primary landmark when the site's normal content is locked out.
|
|
35
43
|
* - Mobile touch targets: The eye toggle button is explicitly sized to 44x44px (w-11 h-11)
|
|
36
44
|
* to satisfy WCAG 2.5.5 minimum tap target requirements.
|
|
37
45
|
* - LocalStorage sync: Checks localStorage on initial load to avoid flash of locked state if previously unlocked.
|
|
38
|
-
* -
|
|
46
|
+
* - Scoped styling: Uses explicit scoped classes (.pnp-pg-submit-btn, .pnp-pg-card, .pnp-pg-message) with CSS variable fallbacks so buttons and cards retain contrast even if consumer Tailwind purges external package utility classes.
|
|
47
|
+
* - Graceful exit: Triggers an opacity fade-out before removing itself from the DOM tree.
|
|
39
48
|
*/
|
|
40
|
-
declare function PasswordGate({ title, subtitle, datePrefix, underConstruction, placeholder, buttonText, enabled, password,
|
|
49
|
+
declare function PasswordGate({ title, subtitle, message, datePrefix, underConstruction, releaseDate, placeholder, buttonText, enabled, password, lang, onUnlock, showPasswordLabel, hidePasswordLabel, unlockBodyClass }: PasswordGateProps): React.JSX.Element;
|
|
41
50
|
|
|
42
51
|
export { type PasswordGateProps, PasswordGate as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,13 +3,15 @@ import React from 'react';
|
|
|
3
3
|
interface PasswordGateProps {
|
|
4
4
|
title: string;
|
|
5
5
|
subtitle: string;
|
|
6
|
-
datePrefix: string;
|
|
7
|
-
underConstruction: string;
|
|
8
6
|
placeholder: string;
|
|
9
7
|
buttonText: string;
|
|
8
|
+
message?: string;
|
|
9
|
+
datePrefix?: string;
|
|
10
|
+
underConstruction?: string;
|
|
11
|
+
releaseDate?: string;
|
|
10
12
|
enabled?: boolean;
|
|
11
13
|
password?: string;
|
|
12
|
-
|
|
14
|
+
lang?: string;
|
|
13
15
|
onUnlock?: () => void;
|
|
14
16
|
showPasswordLabel?: string;
|
|
15
17
|
hidePasswordLabel?: string;
|
|
@@ -19,24 +21,31 @@ interface PasswordGateProps {
|
|
|
19
21
|
* PasswordGate
|
|
20
22
|
*
|
|
21
23
|
* Why:
|
|
22
|
-
* Full-screen modal overlay that restricts site access during
|
|
24
|
+
* Full-screen modal overlay that restricts site access during pre-launch or staging phases.
|
|
23
25
|
* Validates user input against a configured password, persists unlock state in localStorage, and
|
|
24
26
|
* removes the locking CSS class from the document root.
|
|
25
27
|
*
|
|
26
28
|
* Inputs:
|
|
27
|
-
* - props: PasswordGateProps including localized copy strings,
|
|
29
|
+
* - props: PasswordGateProps including localized copy strings (title, subtitle, message, placeholder, buttonText),
|
|
30
|
+
* legacy date props (datePrefix, releaseDate, underConstruction), configuration (enabled, password, unlockBodyClass, lang),
|
|
31
|
+
* and callback (onUnlock).
|
|
28
32
|
*
|
|
29
33
|
* Output:
|
|
30
34
|
* - JSX.Element | null (renders null if disabled or already unlocked).
|
|
31
35
|
*
|
|
32
36
|
* Edge cases & handling:
|
|
37
|
+
* - Fallback copy: If `message` is passed, it takes precedence. If `releaseDate` is passed, it preserves
|
|
38
|
+
* legacy `{datePrefix} {releaseDate} {underConstruction}` display. If neither is passed, it detects
|
|
39
|
+
* locale (via `lang` prop or document.documentElement.lang) and renders "¡Próximamente!" for Spanish
|
|
40
|
+
* or "Coming soon!" for English.
|
|
33
41
|
* - Accessibility landmark: Wraps the gate in <main id="main-content" role="main"> so automated audits
|
|
34
42
|
* and screen readers detect a primary landmark when the site's normal content is locked out.
|
|
35
43
|
* - Mobile touch targets: The eye toggle button is explicitly sized to 44x44px (w-11 h-11)
|
|
36
44
|
* to satisfy WCAG 2.5.5 minimum tap target requirements.
|
|
37
45
|
* - LocalStorage sync: Checks localStorage on initial load to avoid flash of locked state if previously unlocked.
|
|
38
|
-
* -
|
|
46
|
+
* - Scoped styling: Uses explicit scoped classes (.pnp-pg-submit-btn, .pnp-pg-card, .pnp-pg-message) with CSS variable fallbacks so buttons and cards retain contrast even if consumer Tailwind purges external package utility classes.
|
|
47
|
+
* - Graceful exit: Triggers an opacity fade-out before removing itself from the DOM tree.
|
|
39
48
|
*/
|
|
40
|
-
declare function PasswordGate({ title, subtitle, datePrefix, underConstruction, placeholder, buttonText, enabled, password,
|
|
49
|
+
declare function PasswordGate({ title, subtitle, message, datePrefix, underConstruction, releaseDate, placeholder, buttonText, enabled, password, lang, onUnlock, showPasswordLabel, hidePasswordLabel, unlockBodyClass }: PasswordGateProps): React.JSX.Element;
|
|
41
50
|
|
|
42
51
|
export { type PasswordGateProps, PasswordGate as default };
|
package/dist/index.js
CHANGED
|
@@ -36,13 +36,15 @@ var import_react = __toESM(require("react"));
|
|
|
36
36
|
function PasswordGate({
|
|
37
37
|
title,
|
|
38
38
|
subtitle,
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
message,
|
|
40
|
+
datePrefix = "",
|
|
41
|
+
underConstruction = "",
|
|
42
|
+
releaseDate = "",
|
|
41
43
|
placeholder,
|
|
42
44
|
buttonText,
|
|
43
45
|
enabled = true,
|
|
44
46
|
password = "",
|
|
45
|
-
|
|
47
|
+
lang,
|
|
46
48
|
onUnlock,
|
|
47
49
|
showPasswordLabel = "Show password",
|
|
48
50
|
hidePasswordLabel = "Hide password",
|
|
@@ -82,6 +84,16 @@ function PasswordGate({
|
|
|
82
84
|
setTimeout(() => setError(false), 500);
|
|
83
85
|
}
|
|
84
86
|
};
|
|
87
|
+
const getDisplayMessage = () => {
|
|
88
|
+
if (message) {
|
|
89
|
+
return message;
|
|
90
|
+
}
|
|
91
|
+
if (releaseDate && releaseDate.trim()) {
|
|
92
|
+
return `${datePrefix} ${releaseDate} ${underConstruction}`.trim();
|
|
93
|
+
}
|
|
94
|
+
const isSpanish = lang?.toLowerCase().startsWith("es") || typeof document !== "undefined" && document.documentElement.lang?.toLowerCase().startsWith("es");
|
|
95
|
+
return isSpanish ? "\xA1Pr\xF3ximamente!" : "Coming soon!";
|
|
96
|
+
};
|
|
85
97
|
return /* @__PURE__ */ import_react.default.createElement(
|
|
86
98
|
"main",
|
|
87
99
|
{
|
|
@@ -89,7 +101,7 @@ function PasswordGate({
|
|
|
89
101
|
role: "main",
|
|
90
102
|
className: `fixed inset-0 z-50 flex items-center justify-center bg-base transition-opacity duration-500 ${unlocked ? "opacity-0 pointer-events-none" : "opacity-100"}`
|
|
91
103
|
},
|
|
92
|
-
/* @__PURE__ */ import_react.default.createElement("div", { className: "max-w-2xl px-6 text-center" }, /* @__PURE__ */ import_react.default.createElement("h1", { className: "text-4xl md:text-5xl font-bold text-heading mb-6" }, title), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-lg text-body mb-8" }, subtitle), /* @__PURE__ */ import_react.default.createElement("div", { className: "bg-surface p-8 rounded-xl shadow-lg inline-block border border-accent-muted" }, /* @__PURE__ */ import_react.default.createElement("p", { className: "text-accent-muted font-bold mb-4" },
|
|
104
|
+
/* @__PURE__ */ import_react.default.createElement("div", { className: "max-w-2xl px-6 text-center" }, /* @__PURE__ */ import_react.default.createElement("h1", { className: "text-4xl md:text-5xl font-bold text-heading mb-6" }, title), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-lg text-body mb-8" }, subtitle), /* @__PURE__ */ import_react.default.createElement("div", { className: "pnp-pg-card bg-surface p-8 rounded-xl shadow-lg inline-block border border-accent-muted" }, /* @__PURE__ */ import_react.default.createElement("p", { className: "pnp-pg-message text-accent-muted font-bold mb-4" }, getDisplayMessage()), /* @__PURE__ */ import_react.default.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col items-center gap-4" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ import_react.default.createElement(
|
|
93
105
|
"input",
|
|
94
106
|
{
|
|
95
107
|
type: showPassword ? "text" : "password",
|
|
@@ -111,7 +123,7 @@ function PasswordGate({
|
|
|
111
123
|
"button",
|
|
112
124
|
{
|
|
113
125
|
type: "submit",
|
|
114
|
-
className: "px-6 py-2 bg-accent-muted text-white rounded font-semibold transition-colors"
|
|
126
|
+
className: "pnp-pg-submit-btn px-6 py-2 bg-accent-muted text-white rounded font-semibold transition-colors"
|
|
115
127
|
},
|
|
116
128
|
buttonText
|
|
117
129
|
)))),
|
|
@@ -125,6 +137,20 @@ function PasswordGate({
|
|
|
125
137
|
align-items: center !important;
|
|
126
138
|
justify-content: center !important;
|
|
127
139
|
}
|
|
140
|
+
.pnp-pg-submit-btn {
|
|
141
|
+
background-color: var(--color-accent-muted, var(--accent-muted, #115e54)) !important;
|
|
142
|
+
color: #ffffff !important;
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
}
|
|
145
|
+
.pnp-pg-submit-btn:hover {
|
|
146
|
+
opacity: 0.9;
|
|
147
|
+
}
|
|
148
|
+
.pnp-pg-card {
|
|
149
|
+
border-color: var(--color-accent-muted, var(--accent-muted, #115e54));
|
|
150
|
+
}
|
|
151
|
+
.pnp-pg-message {
|
|
152
|
+
color: var(--color-accent-muted, var(--accent-muted, #115e54));
|
|
153
|
+
}
|
|
128
154
|
@keyframes shake {
|
|
129
155
|
0%, 100% { transform: translateX(0); }
|
|
130
156
|
25% { transform: translateX(-5px); }
|
package/dist/index.mjs
CHANGED
|
@@ -3,13 +3,15 @@ import React, { useState } from "react";
|
|
|
3
3
|
function PasswordGate({
|
|
4
4
|
title,
|
|
5
5
|
subtitle,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
message,
|
|
7
|
+
datePrefix = "",
|
|
8
|
+
underConstruction = "",
|
|
9
|
+
releaseDate = "",
|
|
8
10
|
placeholder,
|
|
9
11
|
buttonText,
|
|
10
12
|
enabled = true,
|
|
11
13
|
password = "",
|
|
12
|
-
|
|
14
|
+
lang,
|
|
13
15
|
onUnlock,
|
|
14
16
|
showPasswordLabel = "Show password",
|
|
15
17
|
hidePasswordLabel = "Hide password",
|
|
@@ -49,6 +51,16 @@ function PasswordGate({
|
|
|
49
51
|
setTimeout(() => setError(false), 500);
|
|
50
52
|
}
|
|
51
53
|
};
|
|
54
|
+
const getDisplayMessage = () => {
|
|
55
|
+
if (message) {
|
|
56
|
+
return message;
|
|
57
|
+
}
|
|
58
|
+
if (releaseDate && releaseDate.trim()) {
|
|
59
|
+
return `${datePrefix} ${releaseDate} ${underConstruction}`.trim();
|
|
60
|
+
}
|
|
61
|
+
const isSpanish = lang?.toLowerCase().startsWith("es") || typeof document !== "undefined" && document.documentElement.lang?.toLowerCase().startsWith("es");
|
|
62
|
+
return isSpanish ? "\xA1Pr\xF3ximamente!" : "Coming soon!";
|
|
63
|
+
};
|
|
52
64
|
return /* @__PURE__ */ React.createElement(
|
|
53
65
|
"main",
|
|
54
66
|
{
|
|
@@ -56,7 +68,7 @@ function PasswordGate({
|
|
|
56
68
|
role: "main",
|
|
57
69
|
className: `fixed inset-0 z-50 flex items-center justify-center bg-base transition-opacity duration-500 ${unlocked ? "opacity-0 pointer-events-none" : "opacity-100"}`
|
|
58
70
|
},
|
|
59
|
-
/* @__PURE__ */ React.createElement("div", { className: "max-w-2xl px-6 text-center" }, /* @__PURE__ */ React.createElement("h1", { className: "text-4xl md:text-5xl font-bold text-heading mb-6" }, title), /* @__PURE__ */ React.createElement("p", { className: "text-lg text-body mb-8" }, subtitle), /* @__PURE__ */ React.createElement("div", { className: "bg-surface p-8 rounded-xl shadow-lg inline-block border border-accent-muted" }, /* @__PURE__ */ React.createElement("p", { className: "text-accent-muted font-bold mb-4" },
|
|
71
|
+
/* @__PURE__ */ React.createElement("div", { className: "max-w-2xl px-6 text-center" }, /* @__PURE__ */ React.createElement("h1", { className: "text-4xl md:text-5xl font-bold text-heading mb-6" }, title), /* @__PURE__ */ React.createElement("p", { className: "text-lg text-body mb-8" }, subtitle), /* @__PURE__ */ React.createElement("div", { className: "pnp-pg-card bg-surface p-8 rounded-xl shadow-lg inline-block border border-accent-muted" }, /* @__PURE__ */ React.createElement("p", { className: "pnp-pg-message text-accent-muted font-bold mb-4" }, getDisplayMessage()), /* @__PURE__ */ React.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col items-center gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React.createElement(
|
|
60
72
|
"input",
|
|
61
73
|
{
|
|
62
74
|
type: showPassword ? "text" : "password",
|
|
@@ -78,7 +90,7 @@ function PasswordGate({
|
|
|
78
90
|
"button",
|
|
79
91
|
{
|
|
80
92
|
type: "submit",
|
|
81
|
-
className: "px-6 py-2 bg-accent-muted text-white rounded font-semibold transition-colors"
|
|
93
|
+
className: "pnp-pg-submit-btn px-6 py-2 bg-accent-muted text-white rounded font-semibold transition-colors"
|
|
82
94
|
},
|
|
83
95
|
buttonText
|
|
84
96
|
)))),
|
|
@@ -92,6 +104,20 @@ function PasswordGate({
|
|
|
92
104
|
align-items: center !important;
|
|
93
105
|
justify-content: center !important;
|
|
94
106
|
}
|
|
107
|
+
.pnp-pg-submit-btn {
|
|
108
|
+
background-color: var(--color-accent-muted, var(--accent-muted, #115e54)) !important;
|
|
109
|
+
color: #ffffff !important;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
}
|
|
112
|
+
.pnp-pg-submit-btn:hover {
|
|
113
|
+
opacity: 0.9;
|
|
114
|
+
}
|
|
115
|
+
.pnp-pg-card {
|
|
116
|
+
border-color: var(--color-accent-muted, var(--accent-muted, #115e54));
|
|
117
|
+
}
|
|
118
|
+
.pnp-pg-message {
|
|
119
|
+
color: var(--color-accent-muted, var(--accent-muted, #115e54));
|
|
120
|
+
}
|
|
95
121
|
@keyframes shake {
|
|
96
122
|
0%, 100% { transform: translateX(0); }
|
|
97
123
|
25% { transform: translateX(-5px); }
|