@burdenoff/microfe-hookmovies 2026.801.1 → 2026.829.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/dist/hookmovies/HookMoviesRoot.d.ts.map +1 -1
- package/dist/hookmovies/HookMoviesRoot.js +51 -54
- package/dist/hookmovies/HookMoviesRoot.js.map +1 -1
- package/dist/hookmovies/components/AddToListButton.d.ts +17 -0
- package/dist/hookmovies/components/AddToListButton.d.ts.map +1 -0
- package/dist/hookmovies/components/AddToListButton.js +217 -0
- package/dist/hookmovies/components/AddToListButton.js.map +1 -0
- package/dist/hookmovies/components/Pagination.d.ts.map +1 -1
- package/dist/hookmovies/components/Pagination.js +32 -31
- package/dist/hookmovies/components/Pagination.js.map +1 -1
- package/dist/hookmovies/components/StateViews.d.ts.map +1 -1
- package/dist/hookmovies/components/StateViews.js +39 -32
- package/dist/hookmovies/components/StateViews.js.map +1 -1
- package/dist/hookmovies/components/index.d.ts +1 -0
- package/dist/hookmovies/components/index.d.ts.map +1 -1
- package/dist/hookmovies/components/index.js +1 -0
- package/dist/hookmovies/hooks/index.d.ts +2 -2
- package/dist/hookmovies/hooks/index.d.ts.map +1 -1
- package/dist/hookmovies/hooks/useHookMoviesApi.d.ts +50 -3
- package/dist/hookmovies/hooks/useHookMoviesApi.d.ts.map +1 -1
- package/dist/hookmovies/hooks/useHookMoviesApi.js +124 -89
- package/dist/hookmovies/hooks/useHookMoviesApi.js.map +1 -1
- package/dist/hookmovies/index.d.ts +2 -2
- package/dist/hookmovies/index.d.ts.map +1 -1
- package/dist/hookmovies/pages/HomePage.d.ts.map +1 -1
- package/dist/hookmovies/pages/HomePage.js +148 -141
- package/dist/hookmovies/pages/HomePage.js.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/catalog/TitlesPage.js +165 -153
- package/dist/hookmovies/pages/catalog/TitlesPage.js.map +1 -1
- package/dist/hookmovies/pages/consumer/DiscoveryPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/consumer/DiscoveryPage.js +169 -146
- package/dist/hookmovies/pages/consumer/DiscoveryPage.js.map +1 -1
- package/dist/hookmovies/pages/consumer/TitleViewPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/consumer/TitleViewPage.js +149 -141
- package/dist/hookmovies/pages/consumer/TitleViewPage.js.map +1 -1
- package/dist/hookmovies/pages/engagement/ReviewsPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/engagement/ReviewsPage.js +205 -101
- package/dist/hookmovies/pages/engagement/ReviewsPage.js.map +1 -1
- package/dist/hookmovies/pages/library/ListsPage.d.ts.map +1 -1
- package/dist/hookmovies/pages/library/ListsPage.js +340 -184
- package/dist/hookmovies/pages/library/ListsPage.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/shared/hooks/index.d.ts +2 -0
- package/dist/shared/hooks/index.d.ts.map +1 -0
- package/dist/shared/hooks/useTr.d.ts +17 -0
- package/dist/shared/hooks/useTr.d.ts.map +1 -0
- package/dist/shared/hooks/useTr.js +14 -0
- package/dist/shared/hooks/useTr.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HookMoviesRoot.d.ts","sourceRoot":"","sources":["../../src/hookmovies/HookMoviesRoot.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAmBhC,OAAO,KAAK,EAAE,mBAAmB,EAAW,MAAM,SAAS,CAAC;AAY5D,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"HookMoviesRoot.d.ts","sourceRoot":"","sources":["../../src/hookmovies/HookMoviesRoot.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAmBhC,OAAO,KAAK,EAAE,mBAAmB,EAAW,MAAM,SAAS,CAAC;AAY5D,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,CA0IlD,CAAC"}
|
|
@@ -1,129 +1,126 @@
|
|
|
1
1
|
import { HookMoviesProvider as e } from "./context/HookMoviesContext.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useTr as t } from "../shared/hooks/useTr.js";
|
|
3
|
+
import { HookMoviesRoutes as n } from "./HookMoviesRoutes.js";
|
|
4
|
+
import { useEffect as r } from "react";
|
|
4
5
|
import { QueryClient as i, QueryClientProvider as a } from "@tanstack/react-query";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { FeatureFlagProvider as v } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
6
|
+
import { Building2 as o, Clapperboard as s, Film as c, Home as l, ListVideo as u, Megaphone as d, ScrollText as f, Star as p, Ticket as m, Users as h } from "lucide-react";
|
|
7
|
+
import { jsx as g } from "react/jsx-runtime";
|
|
8
|
+
import { FeatureFlagProvider as _ } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
9
9
|
//#region src/hookmovies/HookMoviesRoot.tsx
|
|
10
|
-
var
|
|
10
|
+
var v = new i({ defaultOptions: { queries: {
|
|
11
11
|
staleTime: 300 * 1e3,
|
|
12
12
|
retry: 1
|
|
13
|
-
} } }),
|
|
14
|
-
let { registerNavItems:
|
|
15
|
-
let n = C(e);
|
|
16
|
-
return n === e ? t : n;
|
|
17
|
-
}, [C]), T = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
13
|
+
} } }), y = (i) => {
|
|
14
|
+
let { registerNavItems: y, registerFooterItems: b, basePath: x = "/" } = i, S = t(), C = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
18
15
|
return r(() => {
|
|
19
|
-
if (
|
|
16
|
+
if (y) return y([
|
|
20
17
|
{
|
|
21
18
|
id: "hookmovies-home",
|
|
22
|
-
label:
|
|
23
|
-
path:
|
|
24
|
-
icon: /* @__PURE__ */
|
|
19
|
+
label: S("hookmovies.nav.home", "Overview"),
|
|
20
|
+
path: C(x, ""),
|
|
21
|
+
icon: /* @__PURE__ */ g(l, { size: 20 }),
|
|
25
22
|
order: 1,
|
|
26
23
|
group: "main"
|
|
27
24
|
},
|
|
28
25
|
{
|
|
29
26
|
id: "hookmovies-titles",
|
|
30
|
-
label:
|
|
31
|
-
path:
|
|
32
|
-
icon: /* @__PURE__ */
|
|
27
|
+
label: S("hookmovies.nav.titles", "Catalog"),
|
|
28
|
+
path: C(x, "catalog/titles"),
|
|
29
|
+
icon: /* @__PURE__ */ g(c, { size: 20 }),
|
|
33
30
|
order: 10,
|
|
34
31
|
group: "catalog"
|
|
35
32
|
},
|
|
36
33
|
{
|
|
37
34
|
id: "hookmovies-studios",
|
|
38
|
-
label:
|
|
39
|
-
path:
|
|
40
|
-
icon: /* @__PURE__ */
|
|
35
|
+
label: S("hookmovies.nav.studios", "Studios"),
|
|
36
|
+
path: C(x, "catalog/studios"),
|
|
37
|
+
icon: /* @__PURE__ */ g(o, { size: 20 }),
|
|
41
38
|
order: 11,
|
|
42
39
|
group: "catalog"
|
|
43
40
|
},
|
|
44
41
|
{
|
|
45
42
|
id: "hookmovies-people",
|
|
46
|
-
label:
|
|
47
|
-
path:
|
|
48
|
-
icon: /* @__PURE__ */
|
|
43
|
+
label: S("hookmovies.nav.people", "People"),
|
|
44
|
+
path: C(x, "catalog/people"),
|
|
45
|
+
icon: /* @__PURE__ */ g(h, { size: 20 }),
|
|
49
46
|
order: 12,
|
|
50
47
|
group: "catalog"
|
|
51
48
|
},
|
|
52
49
|
{
|
|
53
50
|
id: "hookmovies-productions",
|
|
54
|
-
label:
|
|
55
|
-
path:
|
|
56
|
-
icon: /* @__PURE__ */
|
|
51
|
+
label: S("hookmovies.nav.productions", "Productions"),
|
|
52
|
+
path: C(x, "productions"),
|
|
53
|
+
icon: /* @__PURE__ */ g(s, { size: 20 }),
|
|
57
54
|
order: 20,
|
|
58
55
|
group: "production"
|
|
59
56
|
},
|
|
60
57
|
{
|
|
61
58
|
id: "hookmovies-auditions",
|
|
62
|
-
label:
|
|
63
|
-
path:
|
|
64
|
-
icon: /* @__PURE__ */
|
|
59
|
+
label: S("hookmovies.nav.auditions", "Auditions"),
|
|
60
|
+
path: C(x, "auditions"),
|
|
61
|
+
icon: /* @__PURE__ */ g(d, { size: 20 }),
|
|
65
62
|
order: 21,
|
|
66
63
|
group: "production"
|
|
67
64
|
},
|
|
68
65
|
{
|
|
69
66
|
id: "hookmovies-reviews",
|
|
70
|
-
label:
|
|
71
|
-
path:
|
|
72
|
-
icon: /* @__PURE__ */
|
|
67
|
+
label: S("hookmovies.nav.reviews", "Reviews"),
|
|
68
|
+
path: C(x, "engagement/reviews"),
|
|
69
|
+
icon: /* @__PURE__ */ g(p, { size: 20 }),
|
|
73
70
|
order: 30,
|
|
74
71
|
group: "audience"
|
|
75
72
|
},
|
|
76
73
|
{
|
|
77
74
|
id: "hookmovies-rights",
|
|
78
|
-
label:
|
|
79
|
-
path:
|
|
80
|
-
icon: /* @__PURE__ */
|
|
75
|
+
label: S("hookmovies.nav.rights", "Rights & Distribution"),
|
|
76
|
+
path: C(x, "distribution/rights"),
|
|
77
|
+
icon: /* @__PURE__ */ g(f, { size: 20 }),
|
|
81
78
|
order: 31,
|
|
82
79
|
group: "audience"
|
|
83
80
|
},
|
|
84
81
|
{
|
|
85
82
|
id: "hookmovies-lists",
|
|
86
|
-
label:
|
|
87
|
-
path:
|
|
88
|
-
icon: /* @__PURE__ */
|
|
83
|
+
label: S("hookmovies.nav.lists", "My Library"),
|
|
84
|
+
path: C(x, "library/lists"),
|
|
85
|
+
icon: /* @__PURE__ */ g(u, { size: 20 }),
|
|
89
86
|
order: 40,
|
|
90
87
|
group: "library"
|
|
91
88
|
},
|
|
92
89
|
{
|
|
93
90
|
id: "hookmovies-entitlements",
|
|
94
|
-
label:
|
|
95
|
-
path:
|
|
96
|
-
icon: /* @__PURE__ */
|
|
91
|
+
label: S("hookmovies.nav.entitlements", "My Entitlements"),
|
|
92
|
+
path: C(x, "library/entitlements"),
|
|
93
|
+
icon: /* @__PURE__ */ g(m, { size: 20 }),
|
|
97
94
|
order: 41,
|
|
98
95
|
group: "library"
|
|
99
96
|
}
|
|
100
97
|
]);
|
|
101
98
|
}, [
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
y,
|
|
100
|
+
x,
|
|
101
|
+
S
|
|
105
102
|
]), r(() => {
|
|
106
|
-
if (
|
|
103
|
+
if (b) return b([{
|
|
107
104
|
id: "hookmovies-status",
|
|
108
|
-
label:
|
|
105
|
+
label: S("hookmovies.footer.ready", "HookMovies Ready"),
|
|
109
106
|
type: "status",
|
|
110
107
|
order: 10,
|
|
111
108
|
section: "left"
|
|
112
109
|
}]);
|
|
113
|
-
}, [
|
|
114
|
-
client:
|
|
115
|
-
children: /* @__PURE__ */ _
|
|
110
|
+
}, [b, S]), /* @__PURE__ */ g(a, {
|
|
111
|
+
client: v,
|
|
112
|
+
children: /* @__PURE__ */ g(_, {
|
|
116
113
|
workspaceId: null,
|
|
117
114
|
gateway: "global",
|
|
118
115
|
defaultEnabled: !0,
|
|
119
|
-
children: /* @__PURE__ */
|
|
116
|
+
children: /* @__PURE__ */ g(e, {
|
|
120
117
|
...i,
|
|
121
|
-
children: /* @__PURE__ */
|
|
118
|
+
children: /* @__PURE__ */ g(n, {})
|
|
122
119
|
})
|
|
123
120
|
})
|
|
124
121
|
});
|
|
125
122
|
};
|
|
126
123
|
//#endregion
|
|
127
|
-
export {
|
|
124
|
+
export { y as HookMoviesRoot };
|
|
128
125
|
|
|
129
126
|
//# sourceMappingURL=HookMoviesRoot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HookMoviesRoot.js","names":[],"sources":["../../src/hookmovies/HookMoviesRoot.tsx"],"sourcesContent":["/**\n * HookMovies Root Component\n * @module hookmovies\n */\n\nimport type { FC } from 'react';\nimport {
|
|
1
|
+
{"version":3,"file":"HookMoviesRoot.js","names":[],"sources":["../../src/hookmovies/HookMoviesRoot.tsx"],"sourcesContent":["/**\n * HookMovies Root Component\n * @module hookmovies\n */\n\nimport type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport {\n Home,\n Film,\n Building2,\n Users,\n Clapperboard,\n Megaphone,\n Star,\n ScrollText,\n ListVideo,\n Ticket,\n} from 'lucide-react';\nimport { HookMoviesProvider } from './context/HookMoviesContext';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useTr } from '../shared/hooks/useTr';\nimport { HookMoviesRoutes } from './HookMoviesRoutes';\nimport type { HookMoviesRootProps, NavItem } from './types';\n\n// Create a query client for this microfrontend\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n retry: 1,\n },\n },\n});\n\nexport const HookMoviesRoot: FC<HookMoviesRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const tr = useTr();\n\n // Helper to join paths correctly\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with shell on mount\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n // Overview\n {\n id: 'hookmovies-home',\n label: tr('hookmovies.nav.home', 'Overview'),\n path: joinPath(basePath, ''),\n icon: <Home size={20} />,\n order: 1,\n group: 'main',\n },\n\n // Catalog\n {\n id: 'hookmovies-titles',\n label: tr('hookmovies.nav.titles', 'Catalog'),\n path: joinPath(basePath, 'catalog/titles'),\n icon: <Film size={20} />,\n order: 10,\n group: 'catalog',\n },\n {\n id: 'hookmovies-studios',\n label: tr('hookmovies.nav.studios', 'Studios'),\n path: joinPath(basePath, 'catalog/studios'),\n icon: <Building2 size={20} />,\n order: 11,\n group: 'catalog',\n },\n {\n id: 'hookmovies-people',\n label: tr('hookmovies.nav.people', 'People'),\n path: joinPath(basePath, 'catalog/people'),\n icon: <Users size={20} />,\n order: 12,\n group: 'catalog',\n },\n\n // Production\n {\n id: 'hookmovies-productions',\n label: tr('hookmovies.nav.productions', 'Productions'),\n path: joinPath(basePath, 'productions'),\n icon: <Clapperboard size={20} />,\n order: 20,\n group: 'production',\n },\n {\n id: 'hookmovies-auditions',\n label: tr('hookmovies.nav.auditions', 'Auditions'),\n path: joinPath(basePath, 'auditions'),\n icon: <Megaphone size={20} />,\n order: 21,\n group: 'production',\n },\n\n // Audience & distribution\n {\n id: 'hookmovies-reviews',\n label: tr('hookmovies.nav.reviews', 'Reviews'),\n path: joinPath(basePath, 'engagement/reviews'),\n icon: <Star size={20} />,\n order: 30,\n group: 'audience',\n },\n {\n id: 'hookmovies-rights',\n label: tr('hookmovies.nav.rights', 'Rights & Distribution'),\n path: joinPath(basePath, 'distribution/rights'),\n icon: <ScrollText size={20} />,\n order: 31,\n group: 'audience',\n },\n\n // Library (self-scoped)\n {\n id: 'hookmovies-lists',\n label: tr('hookmovies.nav.lists', 'My Library'),\n path: joinPath(basePath, 'library/lists'),\n icon: <ListVideo size={20} />,\n order: 40,\n group: 'library',\n },\n {\n id: 'hookmovies-entitlements',\n label: tr('hookmovies.nav.entitlements', 'My Entitlements'),\n path: joinPath(basePath, 'library/entitlements'),\n icon: <Ticket size={20} />,\n order: 41,\n group: 'library',\n },\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n // Register footer items\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'hookmovies-status',\n label: tr('hookmovies.footer.ready', 'HookMovies Ready'),\n type: 'status' as const,\n order: 10,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <HookMoviesProvider {...props}>\n <HookMoviesRoutes />\n </HookMoviesProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
interface AddToListButtonProps {
|
|
4
|
+
/** The catalog title to add / remove. */
|
|
5
|
+
titleId: string;
|
|
6
|
+
/** Used for accessible labels so the control reads well out of context. */
|
|
7
|
+
titleName?: string;
|
|
8
|
+
/**
|
|
9
|
+
* `cta` — a full labelled button for hero / detail call-to-action rows.
|
|
10
|
+
* `icon` — a compact square button for poster cards.
|
|
11
|
+
*/
|
|
12
|
+
variant?: 'cta' | 'icon';
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const AddToListButton: FC<AddToListButtonProps>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=AddToListButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddToListButton.d.ts","sourceRoot":"","sources":["../../../src/hookmovies/components/AddToListButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,EAAE,EAAuC,MAAM,OAAO,CAAC;AA+BrE,UAAU,oBAAoB;IAC5B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC,oBAAoB,CA0UpD,CAAC"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { parseListItems as n, useCreateList as r, useMyLists as i, useUpdateList as a } from "../hooks/useHookMoviesApi.js";
|
|
4
|
+
import { ErrorState as o, LoadingState as s } from "./StateViews.js";
|
|
5
|
+
import { useEffect as c, useState as l } from "react";
|
|
6
|
+
import { Bookmark as u, Check as d, ListPlus as f, ListVideo as p, Loader2 as m, Plus as h, X as g } from "lucide-react";
|
|
7
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
8
|
+
//#region src/hookmovies/components/AddToListButton.tsx
|
|
9
|
+
var b = {
|
|
10
|
+
limit: 100,
|
|
11
|
+
offset: 0
|
|
12
|
+
}, x = ["WATCHLIST", "PLAYLIST"], S = {
|
|
13
|
+
WATCHLIST: u,
|
|
14
|
+
PLAYLIST: p
|
|
15
|
+
}, C = (e) => e.charAt(0) + e.slice(1).toLowerCase().replace(/_/g, " "), w = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", T = "mb-1.5 block text-xs font-medium text-text-secondary", E = ({ titleId: u, titleName: p, variant: E = "cta", className: D }) => {
|
|
16
|
+
let O = e(), [k, A] = l(!1), [j, M] = l(!1), [N, P] = l(null), [F, I] = l(null), [L, R] = l(""), [z, B] = l("WATCHLIST"), V = i(void 0, b), H = a(), U = r(), W = V.data?.items ?? [], G = W.filter((e) => n(e.items).some((e) => e.titleId === u)).length > 0;
|
|
17
|
+
c(() => {
|
|
18
|
+
if (!k) return;
|
|
19
|
+
let e = (e) => {
|
|
20
|
+
e.key === "Escape" && A(!1);
|
|
21
|
+
};
|
|
22
|
+
return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
|
|
23
|
+
}, [k]);
|
|
24
|
+
let K = () => {
|
|
25
|
+
A(!1), M(!1), I(null);
|
|
26
|
+
}, q = async (e) => {
|
|
27
|
+
I(null), P(e.id);
|
|
28
|
+
let t = n(e.items), r = t.some((e) => e.titleId === u) ? t.filter((e) => e.titleId !== u) : [...t, {
|
|
29
|
+
titleId: u,
|
|
30
|
+
note: null
|
|
31
|
+
}];
|
|
32
|
+
try {
|
|
33
|
+
await H.mutateAsync({
|
|
34
|
+
id: e.id,
|
|
35
|
+
input: { items: r }
|
|
36
|
+
});
|
|
37
|
+
} catch (e) {
|
|
38
|
+
I(e instanceof Error ? e.message : O("hookmovies.list.updateError", "Failed to update the list (you can only edit your own)"));
|
|
39
|
+
} finally {
|
|
40
|
+
P(null);
|
|
41
|
+
}
|
|
42
|
+
}, J = async (e) => {
|
|
43
|
+
if (e.preventDefault(), I(null), !L.trim()) {
|
|
44
|
+
I(O("hookmovies.list.nameRequired", "Name is required."));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
await U.mutateAsync({
|
|
49
|
+
name: L.trim(),
|
|
50
|
+
kind: z,
|
|
51
|
+
visibility: "PRIVATE",
|
|
52
|
+
items: [{
|
|
53
|
+
titleId: u,
|
|
54
|
+
note: null
|
|
55
|
+
}]
|
|
56
|
+
}), R(""), B("WATCHLIST"), M(!1);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
I(e instanceof Error ? e.message : O("hookmovies.list.createError", "Failed to create the list"));
|
|
59
|
+
}
|
|
60
|
+
}, Y = p ? ` ${p}` : "", X = G ? O("hookmovies.list.inYourLists", "In your lists") : O("hookmovies.list.addToList", "Add to List");
|
|
61
|
+
return /* @__PURE__ */ y(_, { children: [E === "icon" ? /* @__PURE__ */ v("button", {
|
|
62
|
+
type: "button",
|
|
63
|
+
onClick: () => A(!0),
|
|
64
|
+
"aria-label": `${X}${Y ? ` —${Y}` : ""}`,
|
|
65
|
+
title: X,
|
|
66
|
+
className: t("inline-flex size-8 items-center justify-center rounded-lg border border-border-seam", "bg-bg-overlay/80 text-text-secondary backdrop-blur-sm transition-colors", "hover:border-border-strong hover:text-text-primary", "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", G && "text-status-success-text", D),
|
|
67
|
+
children: v(G ? d : f, { className: "h-4 w-4" })
|
|
68
|
+
}) : /* @__PURE__ */ y("button", {
|
|
69
|
+
type: "button",
|
|
70
|
+
onClick: () => A(!0),
|
|
71
|
+
"aria-label": `${X}${Y ? ` —${Y}` : ""}`,
|
|
72
|
+
className: t("inline-flex items-center gap-2 rounded-xl border border-border-seam bg-bg-surface/60", "px-5 py-2.5 text-sm font-semibold text-text-primary backdrop-blur transition-colors", "hover:bg-bg-surface focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", D),
|
|
73
|
+
children: [G ? /* @__PURE__ */ v(d, { className: "h-5 w-5 text-status-success-text" }) : /* @__PURE__ */ v(h, { className: "h-5 w-5" }), X]
|
|
74
|
+
}), k && /* @__PURE__ */ v("div", {
|
|
75
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-canvas/90 p-4 backdrop-blur-sm",
|
|
76
|
+
role: "dialog",
|
|
77
|
+
"aria-modal": "true",
|
|
78
|
+
"aria-label": O("hookmovies.list.dialogAria", "Add{{title}} to a list", { title: Y }),
|
|
79
|
+
onClick: K,
|
|
80
|
+
children: /* @__PURE__ */ y("div", {
|
|
81
|
+
className: "relative w-full max-w-md overflow-hidden rounded-2xl border border-border-seam bg-bg-surface shadow-2xl",
|
|
82
|
+
onClick: (e) => e.stopPropagation(),
|
|
83
|
+
children: [/* @__PURE__ */ y("div", {
|
|
84
|
+
className: "flex items-start justify-between gap-3 border-b border-border-seam px-5 py-4",
|
|
85
|
+
children: [/* @__PURE__ */ y("div", {
|
|
86
|
+
className: "min-w-0",
|
|
87
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
88
|
+
className: "text-sm font-semibold text-text-primary",
|
|
89
|
+
children: O("hookmovies.list.dialogTitle", "Add to a list")
|
|
90
|
+
}), /* @__PURE__ */ v("p", {
|
|
91
|
+
className: "mt-0.5 line-clamp-1 text-xs text-text-secondary",
|
|
92
|
+
children: p ?? O("hookmovies.list.dialogDescription", "Pick one of your watchlists or playlists.")
|
|
93
|
+
})]
|
|
94
|
+
}), /* @__PURE__ */ v("button", {
|
|
95
|
+
type: "button",
|
|
96
|
+
onClick: K,
|
|
97
|
+
"aria-label": O("common.close", "Close"),
|
|
98
|
+
className: "rounded-md p-1 text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
99
|
+
children: /* @__PURE__ */ v(g, { className: "h-4 w-4" })
|
|
100
|
+
})]
|
|
101
|
+
}), /* @__PURE__ */ y("div", {
|
|
102
|
+
className: "px-5 py-4",
|
|
103
|
+
children: [
|
|
104
|
+
F && /* @__PURE__ */ v("div", {
|
|
105
|
+
role: "alert",
|
|
106
|
+
className: "mb-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text",
|
|
107
|
+
children: F
|
|
108
|
+
}),
|
|
109
|
+
V.isLoading ? /* @__PURE__ */ v(s, {
|
|
110
|
+
label: O("hookmovies.list.loading", "Loading your lists…"),
|
|
111
|
+
className: "min-h-[8rem]"
|
|
112
|
+
}) : V.isError ? /* @__PURE__ */ v(o, {
|
|
113
|
+
title: O("hookmovies.list.loadError", "Couldn't load your lists"),
|
|
114
|
+
onRetry: () => void V.refetch(),
|
|
115
|
+
className: "min-h-[8rem]"
|
|
116
|
+
}) : W.length === 0 ? /* @__PURE__ */ v("p", {
|
|
117
|
+
className: "mb-3 text-sm text-text-secondary",
|
|
118
|
+
children: O("hookmovies.list.empty", "You don't have any lists yet — create one below and this title goes straight into it.")
|
|
119
|
+
}) : /* @__PURE__ */ v("ul", {
|
|
120
|
+
className: "-mx-1 max-h-[45vh] space-y-1 overflow-y-auto px-1",
|
|
121
|
+
children: W.map((e) => {
|
|
122
|
+
let r = S[e.kind], i = n(e.items).some((e) => e.titleId === u), a = N === e.id;
|
|
123
|
+
return /* @__PURE__ */ v("li", { children: /* @__PURE__ */ y("button", {
|
|
124
|
+
type: "button",
|
|
125
|
+
onClick: () => void q(e),
|
|
126
|
+
disabled: a,
|
|
127
|
+
"aria-pressed": i,
|
|
128
|
+
className: t("flex w-full items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors", "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", "disabled:opacity-60", i ? "border-status-success-border bg-status-success-bg-subtle" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken"),
|
|
129
|
+
children: [
|
|
130
|
+
/* @__PURE__ */ v(r, { className: "h-4 w-4 shrink-0 text-text-secondary" }),
|
|
131
|
+
/* @__PURE__ */ y("span", {
|
|
132
|
+
className: "min-w-0 flex-1",
|
|
133
|
+
children: [/* @__PURE__ */ v("span", {
|
|
134
|
+
className: "line-clamp-1 block text-sm font-medium text-text-primary",
|
|
135
|
+
children: e.name
|
|
136
|
+
}), /* @__PURE__ */ y("span", {
|
|
137
|
+
className: "block text-[11px] text-text-secondary",
|
|
138
|
+
children: [
|
|
139
|
+
C(e.kind),
|
|
140
|
+
" · ",
|
|
141
|
+
e.itemCount,
|
|
142
|
+
" ",
|
|
143
|
+
e.itemCount === 1 ? O("hookmovies.list.item", "item") : O("hookmovies.list.items", "items")
|
|
144
|
+
]
|
|
145
|
+
})]
|
|
146
|
+
}),
|
|
147
|
+
a ? /* @__PURE__ */ v(m, { className: "h-4 w-4 shrink-0 animate-spin text-text-secondary" }) : i ? /* @__PURE__ */ v(d, { className: "h-4 w-4 shrink-0 text-status-success-text" }) : /* @__PURE__ */ v(h, { className: "h-4 w-4 shrink-0 text-text-secondary" })
|
|
148
|
+
]
|
|
149
|
+
}) }, e.id);
|
|
150
|
+
})
|
|
151
|
+
}),
|
|
152
|
+
!V.isLoading && !V.isError && /* @__PURE__ */ v("div", {
|
|
153
|
+
className: "mt-3 border-t border-border-seam pt-3",
|
|
154
|
+
children: j || W.length === 0 ? /* @__PURE__ */ y("form", {
|
|
155
|
+
onSubmit: (e) => void J(e),
|
|
156
|
+
className: "space-y-3",
|
|
157
|
+
children: [
|
|
158
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ y("label", {
|
|
159
|
+
htmlFor: `add-to-list-name-${u}`,
|
|
160
|
+
className: T,
|
|
161
|
+
children: [O("hookmovies.list.newListName", "New list name"), " *"]
|
|
162
|
+
}), /* @__PURE__ */ v("input", {
|
|
163
|
+
id: `add-to-list-name-${u}`,
|
|
164
|
+
type: "text",
|
|
165
|
+
value: L,
|
|
166
|
+
onChange: (e) => R(e.target.value),
|
|
167
|
+
placeholder: O("hookmovies.list.newListPlaceholder", "e.g. Weekend Watch"),
|
|
168
|
+
className: w,
|
|
169
|
+
autoFocus: !0,
|
|
170
|
+
required: !0
|
|
171
|
+
})] }),
|
|
172
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
173
|
+
htmlFor: `add-to-list-kind-${u}`,
|
|
174
|
+
className: T,
|
|
175
|
+
children: O("hookmovies.list.kind", "Kind")
|
|
176
|
+
}), /* @__PURE__ */ v("select", {
|
|
177
|
+
id: `add-to-list-kind-${u}`,
|
|
178
|
+
value: z,
|
|
179
|
+
onChange: (e) => B(e.target.value),
|
|
180
|
+
className: w,
|
|
181
|
+
children: x.map((e) => /* @__PURE__ */ v("option", {
|
|
182
|
+
value: e,
|
|
183
|
+
children: C(e)
|
|
184
|
+
}, e))
|
|
185
|
+
})] }),
|
|
186
|
+
/* @__PURE__ */ y("div", {
|
|
187
|
+
className: "flex items-center gap-2",
|
|
188
|
+
children: [/* @__PURE__ */ y("button", {
|
|
189
|
+
type: "submit",
|
|
190
|
+
disabled: U.isPending,
|
|
191
|
+
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
192
|
+
children: [/* @__PURE__ */ v(h, { className: "h-3.5 w-3.5" }), U.isPending ? O("hookmovies.list.creating", "Creating…") : O("hookmovies.list.createAndAdd", "Create & add")]
|
|
193
|
+
}), W.length > 0 && /* @__PURE__ */ v("button", {
|
|
194
|
+
type: "button",
|
|
195
|
+
onClick: () => M(!1),
|
|
196
|
+
disabled: U.isPending,
|
|
197
|
+
className: "rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
198
|
+
children: O("common.cancel", "Cancel")
|
|
199
|
+
})]
|
|
200
|
+
})
|
|
201
|
+
]
|
|
202
|
+
}) : /* @__PURE__ */ y("button", {
|
|
203
|
+
type: "button",
|
|
204
|
+
onClick: () => M(!0),
|
|
205
|
+
className: "inline-flex items-center gap-2 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded",
|
|
206
|
+
children: [/* @__PURE__ */ v(h, { className: "h-3.5 w-3.5" }), O("hookmovies.list.newList", "New list")]
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
]
|
|
210
|
+
})]
|
|
211
|
+
})
|
|
212
|
+
})] });
|
|
213
|
+
};
|
|
214
|
+
//#endregion
|
|
215
|
+
export { E as AddToListButton };
|
|
216
|
+
|
|
217
|
+
//# sourceMappingURL=AddToListButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddToListButton.js","names":[],"sources":["../../../src/hookmovies/components/AddToListButton.tsx"],"sourcesContent":["/**\n * Add-to-list control\n * @module hookmovies/components\n *\n * The affordance that puts a title INTO one of the caller's library lists —\n * the write half of `library/lists`, which could previously only create empty\n * collections.\n *\n * The subgraph has no per-item add/remove mutation: `HokList.items` is an\n * ordered `JSON` array (`[{ titleId, note }]`) written wholesale through\n * `hokUpdateList`, which re-derives `itemCount` from its length. So every\n * toggle here reads the list's current items, adds/removes exactly one entry,\n * and writes the whole array back — untouched entries are always preserved.\n *\n * The picker is a centered modal rather than an anchored popover on purpose:\n * these triggers live inside horizontally-scrolling rails and `overflow`\n * containers that would clip a popover.\n */\n\nimport { type FC, type FormEvent, useEffect, useState } from 'react';\nimport { Bookmark, Check, ListPlus, ListVideo, Loader2, Plus, X } from 'lucide-react';\nimport {\n useMyLists,\n useCreateList,\n useUpdateList,\n parseListItems,\n type HokList,\n type HokListKind,\n} from '../hooks/useHookMoviesApi';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { LoadingState, ErrorState } from './StateViews';\nimport { cn } from '../utils/cn';\n\n/** The subgraph clamps any list page at 100 rows — ask for exactly that. */\nconst LISTS_PAGE = { limit: 100, offset: 0 } as const;\n\nconst KIND_OPTIONS: HokListKind[] = ['WATCHLIST', 'PLAYLIST'];\n\nconst kindIcon: Record<HokListKind, typeof ListVideo> = {\n WATCHLIST: Bookmark,\n PLAYLIST: ListVideo,\n};\n\nconst titleCase = (value: string): string =>\n value.charAt(0) + value.slice(1).toLowerCase().replace(/_/g, ' ');\n\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\ninterface AddToListButtonProps {\n /** The catalog title to add / remove. */\n titleId: string;\n /** Used for accessible labels so the control reads well out of context. */\n titleName?: string;\n /**\n * `cta` — a full labelled button for hero / detail call-to-action rows.\n * `icon` — a compact square button for poster cards.\n */\n variant?: 'cta' | 'icon';\n className?: string;\n}\n\nexport const AddToListButton: FC<AddToListButtonProps> = ({\n titleId,\n titleName,\n variant = 'cta',\n className,\n}) => {\n const tr = useTr();\n const [open, setOpen] = useState(false);\n const [showCreate, setShowCreate] = useState(false);\n const [pendingListId, setPendingListId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n const [newName, setNewName] = useState('');\n const [newKind, setNewKind] = useState<HokListKind>('WATCHLIST');\n\n const lists = useMyLists(undefined, LISTS_PAGE);\n const updateList = useUpdateList();\n const createList = useCreateList();\n\n const items = lists.data?.items ?? [];\n const memberOf = items.filter((list) =>\n parseListItems(list.items).some((item) => item.titleId === titleId)\n );\n const isMember = memberOf.length > 0;\n\n // Close the picker on Escape, mirroring the trailer modal on the title view.\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setOpen(false);\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [open]);\n\n const closeModal = () => {\n setOpen(false);\n setShowCreate(false);\n setActionError(null);\n };\n\n const handleToggle = async (list: HokList) => {\n setActionError(null);\n setPendingListId(list.id);\n const current = parseListItems(list.items);\n const alreadyIn = current.some((item) => item.titleId === titleId);\n const next = alreadyIn\n ? current.filter((item) => item.titleId !== titleId)\n : [...current, { titleId, note: null }];\n try {\n await updateList.mutateAsync({ id: list.id, input: { items: next } });\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\n 'hookmovies.list.updateError',\n 'Failed to update the list (you can only edit your own)'\n )\n );\n } finally {\n setPendingListId(null);\n }\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n setActionError(null);\n if (!newName.trim()) {\n setActionError(tr('hookmovies.list.nameRequired', 'Name is required.'));\n return;\n }\n try {\n await createList.mutateAsync({\n name: newName.trim(),\n kind: newKind,\n visibility: 'PRIVATE',\n items: [{ titleId, note: null }],\n });\n setNewName('');\n setNewKind('WATCHLIST');\n setShowCreate(false);\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr('hookmovies.list.createError', 'Failed to create the list')\n );\n }\n };\n\n const forLabel = titleName ? ` ${titleName}` : '';\n const triggerLabel = isMember\n ? tr('hookmovies.list.inYourLists', 'In your lists')\n : tr('hookmovies.list.addToList', 'Add to List');\n\n return (\n <>\n {variant === 'icon' ? (\n <button\n type=\"button\"\n onClick={() => setOpen(true)}\n aria-label={`${triggerLabel}${forLabel ? ` —${forLabel}` : ''}`}\n title={triggerLabel}\n className={cn(\n 'inline-flex size-8 items-center justify-center rounded-lg border border-border-seam',\n 'bg-bg-overlay/80 text-text-secondary backdrop-blur-sm transition-colors',\n 'hover:border-border-strong hover:text-text-primary',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n isMember && 'text-status-success-text',\n className\n )}\n >\n {isMember ? <Check className=\"h-4 w-4\" /> : <ListPlus className=\"h-4 w-4\" />}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => setOpen(true)}\n aria-label={`${triggerLabel}${forLabel ? ` —${forLabel}` : ''}`}\n className={cn(\n 'inline-flex items-center gap-2 rounded-xl border border-border-seam bg-bg-surface/60',\n 'px-5 py-2.5 text-sm font-semibold text-text-primary backdrop-blur transition-colors',\n 'hover:bg-bg-surface focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n className\n )}\n >\n {isMember ? (\n <Check className=\"h-5 w-5 text-status-success-text\" />\n ) : (\n <Plus className=\"h-5 w-5\" />\n )}\n {triggerLabel}\n </button>\n )}\n\n {open && (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-canvas/90 p-4 backdrop-blur-sm\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={tr('hookmovies.list.dialogAria', 'Add{{title}} to a list', {\n title: forLabel,\n })}\n onClick={closeModal}\n >\n <div\n className=\"relative w-full max-w-md overflow-hidden rounded-2xl border border-border-seam bg-bg-surface shadow-2xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-start justify-between gap-3 border-b border-border-seam px-5 py-4\">\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('hookmovies.list.dialogTitle', 'Add to a list')}\n </h2>\n <p className=\"mt-0.5 line-clamp-1 text-xs text-text-secondary\">\n {titleName ??\n tr(\n 'hookmovies.list.dialogDescription',\n 'Pick one of your watchlists or playlists.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={closeModal}\n aria-label={tr('common.close', 'Close')}\n className=\"rounded-md p-1 text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"px-5 py-4\">\n {actionError && (\n <div\n role=\"alert\"\n className=\"mb-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n\n {lists.isLoading ? (\n <LoadingState\n label={tr('hookmovies.list.loading', 'Loading your lists…')}\n className=\"min-h-[8rem]\"\n />\n ) : lists.isError ? (\n <ErrorState\n title={tr('hookmovies.list.loadError', \"Couldn't load your lists\")}\n onRetry={() => void lists.refetch()}\n className=\"min-h-[8rem]\"\n />\n ) : items.length === 0 ? (\n <p className=\"mb-3 text-sm text-text-secondary\">\n {tr(\n 'hookmovies.list.empty',\n \"You don't have any lists yet — create one below and this title goes straight into it.\"\n )}\n </p>\n ) : (\n <ul className=\"-mx-1 max-h-[45vh] space-y-1 overflow-y-auto px-1\">\n {items.map((list) => {\n const KindIcon = kindIcon[list.kind];\n const inList = parseListItems(list.items).some(\n (item) => item.titleId === titleId\n );\n const busy = pendingListId === list.id;\n return (\n <li key={list.id}>\n <button\n type=\"button\"\n onClick={() => void handleToggle(list)}\n disabled={busy}\n aria-pressed={inList}\n className={cn(\n 'flex w-full items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n 'disabled:opacity-60',\n inList\n ? 'border-status-success-border bg-status-success-bg-subtle'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n )}\n >\n <KindIcon className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"line-clamp-1 block text-sm font-medium text-text-primary\">\n {list.name}\n </span>\n <span className=\"block text-[11px] text-text-secondary\">\n {titleCase(list.kind)} · {list.itemCount}{' '}\n {list.itemCount === 1\n ? tr('hookmovies.list.item', 'item')\n : tr('hookmovies.list.items', 'items')}\n </span>\n </span>\n {busy ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin text-text-secondary\" />\n ) : inList ? (\n <Check className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n ) : (\n <Plus className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n\n {!lists.isLoading && !lists.isError && (\n <div className=\"mt-3 border-t border-border-seam pt-3\">\n {showCreate || items.length === 0 ? (\n <form onSubmit={(e) => void handleCreate(e)} className=\"space-y-3\">\n <div>\n <label htmlFor={`add-to-list-name-${titleId}`} className={labelClass}>\n {tr('hookmovies.list.newListName', 'New list name')} *\n </label>\n <input\n id={`add-to-list-name-${titleId}`}\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder={tr(\n 'hookmovies.list.newListPlaceholder',\n 'e.g. Weekend Watch'\n )}\n className={fieldClass}\n autoFocus\n required\n />\n </div>\n <div>\n <label htmlFor={`add-to-list-kind-${titleId}`} className={labelClass}>\n {tr('hookmovies.list.kind', 'Kind')}\n </label>\n <select\n id={`add-to-list-kind-${titleId}`}\n value={newKind}\n onChange={(e) => setNewKind(e.target.value as HokListKind)}\n className={fieldClass}\n >\n {KIND_OPTIONS.map((k) => (\n <option key={k} value={k}>\n {titleCase(k)}\n </option>\n ))}\n </select>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"submit\"\n disabled={createList.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n {createList.isPending\n ? tr('hookmovies.list.creating', 'Creating…')\n : tr('hookmovies.list.createAndAdd', 'Create & add')}\n </button>\n {items.length > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n disabled={createList.isPending}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n )}\n </div>\n </form>\n ) : (\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"inline-flex items-center gap-2 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n {tr('hookmovies.list.newList', 'New list')}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/hookmovies/components/Pagination.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/hookmovies/components/Pagination.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAKhC,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CA2C1C,CAAC"}
|
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { ChevronLeft as n, ChevronRight as r } from "lucide-react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
5
|
//#region src/hookmovies/components/Pagination.tsx
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
className: "
|
|
9
|
-
children: [
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
6
|
+
var o = ({ page: o, hasMore: s, onPageChange: c, shownCount: l, className: u }) => {
|
|
7
|
+
let d = e();
|
|
8
|
+
return o === 0 && !s ? null : /* @__PURE__ */ a("div", {
|
|
9
|
+
className: t("mt-6 flex items-center justify-between", u),
|
|
10
|
+
children: [/* @__PURE__ */ i("p", {
|
|
11
|
+
className: "text-xs text-text-secondary",
|
|
12
|
+
children: d("hookmovies.pagination.pageInfo", "Page {{page}} · {{count}} shown", {
|
|
13
|
+
page: o + 1,
|
|
14
|
+
count: l
|
|
15
|
+
})
|
|
16
|
+
}), /* @__PURE__ */ a("div", {
|
|
17
|
+
className: "flex items-center gap-2",
|
|
18
|
+
children: [/* @__PURE__ */ a("button", {
|
|
19
|
+
type: "button",
|
|
20
|
+
disabled: o === 0,
|
|
21
|
+
onClick: () => c(o - 1),
|
|
22
|
+
className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary disabled:opacity-40 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors",
|
|
23
|
+
children: [/* @__PURE__ */ i(n, { className: "h-3.5 w-3.5" }), d("hookmovies.pagination.previous", "Previous")]
|
|
24
|
+
}), /* @__PURE__ */ a("button", {
|
|
25
|
+
type: "button",
|
|
26
|
+
disabled: !s,
|
|
27
|
+
onClick: () => c(o + 1),
|
|
28
|
+
className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary disabled:opacity-40 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors",
|
|
29
|
+
children: [d("hookmovies.pagination.next", "Next"), /* @__PURE__ */ i(r, { className: "h-3.5 w-3.5" })]
|
|
30
|
+
})]
|
|
30
31
|
})]
|
|
31
|
-
})
|
|
32
|
-
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
33
34
|
//#endregion
|
|
34
|
-
export {
|
|
35
|
+
export { o as Pagination };
|
|
35
36
|
|
|
36
37
|
//# sourceMappingURL=Pagination.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/hookmovies/components/Pagination.tsx"],"sourcesContent":["/**\n * Cursor/offset pagination control\n * @module hookmovies/components\n *\n * Drives the subgraph's offset-based `PaginationInput`. The svc returns\n * `pageInfo { cursor, hasMore }`; we page with offset = page * limit and use\n * `hasMore` to enable the Next button.\n */\n\nimport type { FC } from 'react';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\ninterface PaginationProps {\n page: number;\n hasMore: boolean;\n onPageChange: (page: number) => void;\n /** Number of rows currently shown (for the \"Showing N\" label). */\n shownCount: number;\n className?: string;\n}\n\nexport const Pagination: FC<PaginationProps> = ({\n page,\n hasMore,\n onPageChange,\n shownCount,\n className,\n}) => {\n if (page === 0 && !hasMore) {\n return null;\n }\n\n return (\n <div className={cn('mt-6 flex items-center justify-between', className)}>\n <p className=\"text-xs text-text-secondary\">\n Page {page
|
|
1
|
+
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/hookmovies/components/Pagination.tsx"],"sourcesContent":["/**\n * Cursor/offset pagination control\n * @module hookmovies/components\n *\n * Drives the subgraph's offset-based `PaginationInput`. The svc returns\n * `pageInfo { cursor, hasMore }`; we page with offset = page * limit and use\n * `hasMore` to enable the Next button.\n */\n\nimport type { FC } from 'react';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { cn } from '../utils/cn';\n\ninterface PaginationProps {\n page: number;\n hasMore: boolean;\n onPageChange: (page: number) => void;\n /** Number of rows currently shown (for the \"Showing N\" label). */\n shownCount: number;\n className?: string;\n}\n\nexport const Pagination: FC<PaginationProps> = ({\n page,\n hasMore,\n onPageChange,\n shownCount,\n className,\n}) => {\n const tr = useTr();\n\n if (page === 0 && !hasMore) {\n return null;\n }\n\n return (\n <div className={cn('mt-6 flex items-center justify-between', className)}>\n <p className=\"text-xs text-text-secondary\">\n {tr('hookmovies.pagination.pageInfo', 'Page {{page}} · {{count}} shown', {\n page: page + 1,\n count: shownCount,\n })}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => onPageChange(page - 1)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary disabled:opacity-40 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n <ChevronLeft className=\"h-3.5 w-3.5\" />\n {tr('hookmovies.pagination.previous', 'Previous')}\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => onPageChange(page + 1)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary disabled:opacity-40 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n {tr('hookmovies.pagination.next', 'Next')}\n <ChevronRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;AAuBA,IAAa,KAAmC,EAC9C,SACA,YACA,iBACA,eACA,mBACI;CACJ,IAAM,IAAK,GAAO;AAMlB,QAJI,MAAS,KAAK,CAAC,IACV,OAIP,kBAAC,OAAD;EAAK,WAAW,EAAG,0CAA0C,EAAU;YAAvE,CACE,kBAAC,KAAD;GAAG,WAAU;aACV,EAAG,kCAAkC,mCAAmC;IACvE,MAAM,IAAO;IACb,OAAO;IACR,CAAC;GACA,CAAA,EACJ,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,UAAU,MAAS;IACnB,eAAe,EAAa,IAAO,EAAE;IACrC,WAAU;cAJZ,CAME,kBAAC,GAAD,EAAa,WAAU,eAAgB,CAAA,EACtC,EAAG,kCAAkC,WAAW,CAC1C;OACT,kBAAC,UAAD;IACE,MAAK;IACL,UAAU,CAAC;IACX,eAAe,EAAa,IAAO,EAAE;IACrC,WAAU;cAJZ,CAMG,EAAG,8BAA8B,OAAO,EACzC,kBAAC,GAAD,EAAc,WAAU,eAAgB,CAAA,CACjC;MACL;KACF"}
|