@parallelworks/tandem-web 0.141.1 → 0.142.0

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.
@@ -0,0 +1,256 @@
1
+ import { usePageTitle as e } from "../../../lib/pageTitle.js";
2
+ import { teamsQuery as t, workspaceQuery as n } from "../../../api/queries.js";
3
+ import { BoardIcon as r, ListViewIcon as i } from "../../../components/icons.js";
4
+ import { Route as a } from "./space._space.js";
5
+ import { usePeople as o } from "../../../lib/people.js";
6
+ import { docPropertiesQuery as s, docViewsQuery as c, readConfig as l, useDocPropertyActions as u } from "../../../lib/docProperties.js";
7
+ import { DocTable as d, groupable as f } from "../../../components/DocTable.js";
8
+ import { Breadcrumbs as p, Button as m, ErrorText as h, Input as g, Popover as _, SegmentedControl as v, Select as y, useClosePopover as b } from "@parallelworks/ui";
9
+ import { useNavigate as x } from "@tanstack/react-router";
10
+ import { useEffect as S, useState as C } from "react";
11
+ import { useErrorMessage as w } from "@parallelworks/problem/react";
12
+ import { useTranslations as T } from "use-intl";
13
+ import { jsx as E, jsxs as D } from "react/jsx-runtime";
14
+ import { useQuery as O, useSuspenseQuery as k } from "@tanstack/react-query";
15
+ // src/routes/$workspace/docs/space.$space.tsx?tsr-split=component
16
+ function A() {
17
+ let o = T("docViews"), g = w(), { workspace: _, space: b } = a.useParams(), { view: A } = a.useSearch(), N = x(), P = (e) => N({
18
+ to: "/$workspace/docs/space/$space",
19
+ params: {
20
+ workspace: _,
21
+ space: b
22
+ },
23
+ search: e ? { view: e } : {}
24
+ }), { data: F } = k(t(_)), { data: I } = k(n(_)), L = b === "workspace" ? I.name : F.find((e) => e.key === b)?.name ?? b;
25
+ e(o("title"), L);
26
+ let { data: R = [] } = O(s(_, b)), { data: z = [] } = O(c(_, b)), B = u(_), V = z.find((e) => e.id === A), [H, U] = C("table"), [W, G] = C({});
27
+ S(() => {
28
+ V && (U(V.layout), G(l(V.config)));
29
+ }, [V]);
30
+ let K = (e) => G(e), q = [
31
+ {
32
+ value: "title",
33
+ label: o("docTitle")
34
+ },
35
+ {
36
+ value: "updated",
37
+ label: o("updated")
38
+ },
39
+ ...R.map((e) => ({
40
+ value: e.id,
41
+ label: e.name
42
+ }))
43
+ ], J = B.saveView.error ?? B.updateView.error;
44
+ return /* @__PURE__ */ D("div", {
45
+ className: "flex h-full flex-col",
46
+ children: [
47
+ /* @__PURE__ */ D("header", {
48
+ className: "flex min-h-12 shrink-0 flex-wrap items-center gap-2 border-b border-border px-4 py-2 md:px-5",
49
+ children: [
50
+ /* @__PURE__ */ E(p, {
51
+ crumbs: [{
52
+ label: o("docs"),
53
+ href: `/${_}/docs`
54
+ }, { label: L }],
55
+ className: "min-w-0 flex-1"
56
+ }),
57
+ /* @__PURE__ */ E(y, {
58
+ value: A ?? "",
59
+ items: [{
60
+ value: "",
61
+ label: o("unsaved")
62
+ }, ...z.map((e) => ({
63
+ value: e.id,
64
+ label: e.name
65
+ }))],
66
+ onValueChange: (e) => P(e || void 0),
67
+ "aria-label": o("savedViews"),
68
+ size: "sm",
69
+ width: "content"
70
+ }),
71
+ V ? /* @__PURE__ */ E(m, {
72
+ size: "sm",
73
+ loading: B.updateView.isPending,
74
+ onClick: () => B.updateView.mutate({
75
+ id: V.id,
76
+ layout: H,
77
+ config: { ...W }
78
+ }),
79
+ children: o("update")
80
+ }) : /* @__PURE__ */ E(M, {
81
+ onSave: async (e) => {
82
+ let t = await B.saveView.mutateAsync({
83
+ space: b,
84
+ name: e,
85
+ layout: H,
86
+ config: { ...W }
87
+ });
88
+ P(t.id);
89
+ },
90
+ pending: B.saveView.isPending
91
+ }),
92
+ /* @__PURE__ */ E(v, {
93
+ label: o("layout"),
94
+ size: "sm",
95
+ value: H,
96
+ onValueChange: U,
97
+ options: [{
98
+ value: "table",
99
+ label: o("table"),
100
+ icon: /* @__PURE__ */ E(i, {})
101
+ }, {
102
+ value: "board",
103
+ label: o("board"),
104
+ icon: /* @__PURE__ */ E(r, {})
105
+ }]
106
+ })
107
+ ]
108
+ }),
109
+ /* @__PURE__ */ D("div", {
110
+ className: "flex flex-wrap items-center gap-2 border-b border-border px-4 py-2 text-xs md:px-5",
111
+ children: [
112
+ H === "board" ? /* @__PURE__ */ E(y, {
113
+ value: W.groupBy ?? "",
114
+ items: [{
115
+ value: "",
116
+ label: o("groupBy")
117
+ }, ...R.filter(f).map((e) => ({
118
+ value: e.id,
119
+ label: e.name
120
+ }))],
121
+ onValueChange: (e) => K({
122
+ ...W,
123
+ ...e ? { groupBy: e } : { groupBy: void 0 }
124
+ }),
125
+ "aria-label": o("groupBy"),
126
+ size: "sm",
127
+ width: "content"
128
+ }) : /* @__PURE__ */ E(y, {
129
+ multiple: !0,
130
+ value: W.columns ?? R.map((e) => e.id),
131
+ items: R.map((e) => ({
132
+ value: e.id,
133
+ label: e.name
134
+ })),
135
+ onValueChange: (e) => K({
136
+ ...W,
137
+ columns: e
138
+ }),
139
+ "aria-label": o("columns"),
140
+ placeholder: o("columns"),
141
+ size: "sm",
142
+ width: "content"
143
+ }),
144
+ /* @__PURE__ */ E(y, {
145
+ value: W.sort ?? "title",
146
+ items: q,
147
+ onValueChange: (e) => K({
148
+ ...W,
149
+ sort: e
150
+ }),
151
+ "aria-label": o("sortBy"),
152
+ size: "sm",
153
+ width: "content"
154
+ }),
155
+ /* @__PURE__ */ E(m, {
156
+ size: "xs",
157
+ variant: "ghost",
158
+ onClick: () => K({
159
+ ...W,
160
+ desc: !W.desc
161
+ }),
162
+ children: W.desc ? o("descending") : o("ascending")
163
+ }),
164
+ R.map((e) => /* @__PURE__ */ E(j, {
165
+ workspace: _,
166
+ property: e,
167
+ value: W.filters?.[e.id] ?? "",
168
+ onChange: (t) => {
169
+ let n = { ...W.filters };
170
+ t ? n[e.id] = t : delete n[e.id], K({
171
+ ...W,
172
+ filters: n
173
+ });
174
+ }
175
+ }, e.id)),
176
+ J && /* @__PURE__ */ E(h, {
177
+ inline: !0,
178
+ children: g(J)
179
+ })
180
+ ]
181
+ }),
182
+ /* @__PURE__ */ E("div", {
183
+ className: "min-h-0 flex-1 overflow-y-auto",
184
+ children: /* @__PURE__ */ E(d, {
185
+ workspace: _,
186
+ space: b,
187
+ layout: H,
188
+ config: W
189
+ })
190
+ })
191
+ ]
192
+ });
193
+ }
194
+ function j({ workspace: e, property: t, value: n, onChange: r }) {
195
+ let i = T("docViews"), { members: a } = o(e), s = t.kind === "select" || t.kind === "multi_select" ? t.options.map((e) => ({
196
+ value: e.id ?? "",
197
+ label: e.name
198
+ })) : t.kind === "person" ? a.map((e) => ({
199
+ value: e.user.username,
200
+ label: e.user.name
201
+ })) : t.kind === "checkbox" ? [{
202
+ value: "true",
203
+ label: i("checked")
204
+ }] : null;
205
+ return s ? /* @__PURE__ */ E(y, {
206
+ value: n,
207
+ items: [{
208
+ value: "",
209
+ label: i("anyValue", { property: t.name })
210
+ }, ...s],
211
+ onValueChange: r,
212
+ "aria-label": i("filterBy", { property: t.name }),
213
+ size: "sm",
214
+ width: "content"
215
+ }) : null;
216
+ }
217
+ function M({ onSave: e, pending: t }) {
218
+ let n = T("docViews");
219
+ return /* @__PURE__ */ E(_, {
220
+ trigger: /* @__PURE__ */ E(m, {
221
+ size: "sm",
222
+ children: n("save")
223
+ }),
224
+ align: "end",
225
+ className: "w-64",
226
+ initialFocus: "first-control",
227
+ "aria-label": n("save"),
228
+ children: /* @__PURE__ */ E(N, {
229
+ onSave: e,
230
+ pending: t
231
+ })
232
+ });
233
+ }
234
+ function N({ onSave: e, pending: t }) {
235
+ let n = T("docViews"), r = b(), [i, a] = C("");
236
+ return /* @__PURE__ */ D("form", {
237
+ className: "space-y-2",
238
+ onSubmit: (t) => {
239
+ t.preventDefault(), i.trim() && (e(i.trim()), r());
240
+ },
241
+ children: [/* @__PURE__ */ E(g, {
242
+ value: i,
243
+ onChange: (e) => a(e.target.value),
244
+ placeholder: n("viewName"),
245
+ "aria-label": n("viewName")
246
+ }), /* @__PURE__ */ E(m, {
247
+ type: "submit",
248
+ size: "sm",
249
+ variant: "primary",
250
+ loading: t,
251
+ disabled: !i.trim(),
252
+ children: n("save")
253
+ })]
254
+ });
255
+ }
256
+ export { A as component };
@@ -5,17 +5,17 @@ import { Outlet as r, useParams as i } from "@tanstack/react-router";
5
5
  import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
6
  // src/routes/$workspace/docs.tsx?tsr-split=component
7
7
  function s() {
8
- let { workspace: s } = e.useParams(), { doc: c } = i({ strict: !1 }), l = c ? t(c) : void 0;
8
+ let { workspace: s } = e.useParams(), { doc: c, space: l } = i({ strict: !1 }), u = c ? t(c) : l;
9
9
  return /* @__PURE__ */ o("div", {
10
10
  className: "flex h-full",
11
- children: [!l && /* @__PURE__ */ a("div", {
11
+ children: [!u && /* @__PURE__ */ a("div", {
12
12
  className: "mx-auto flex w-full max-w-2xl flex-col px-3 py-3 md:py-6",
13
13
  children: /* @__PURE__ */ a(n, {
14
14
  workspace: s,
15
15
  page: !0
16
16
  }, s)
17
17
  }), /* @__PURE__ */ a("div", {
18
- className: `min-w-0 flex-1 ${l ? "block" : "hidden"}`,
18
+ className: `min-w-0 flex-1 ${u ? "block" : "hidden"}`,
19
19
  children: /* @__PURE__ */ a(r, {})
20
20
  })]
21
21
  });
@@ -45,8 +45,8 @@ import { AppFrame as nt, AppFrameToggle as I, useAppFrame as L } from "@parallel
45
45
  import { createPortal as R } from "react-dom";
46
46
  // src/routes/$workspace.tsx?tsr-split=component
47
47
  function rt(e, t) {
48
- let n = e.slice(`/${t}/docs/`.length).split("/")[0];
49
- return e.startsWith(`/${t}/docs/`) && n ? h(n) : void 0;
48
+ let [n, r] = e.slice(`/${t}/docs/`.length).split("/");
49
+ return e.startsWith(`/${t}/docs/`) && n && r === void 0 ? h(n) : void 0;
50
50
  }
51
51
  var z = { className: v }, B = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-[color-mix(in_oklab,currentColor_20%,transparent)] bg-[color-mix(in_oklab,currentColor_6%,transparent)] text-sm font-medium hover:bg-[color-mix(in_oklab,currentColor_12%,transparent)]", it = "absolute top-1.5 right-3 size-2 rounded-full bg-primary", V = (...e) => e.filter(Boolean).join(" ");
52
52
  function H() {
package/styles.css CHANGED
@@ -523,6 +523,23 @@ body[data-tandem] > :not(:has(.tandem)) {
523
523
  color: inherit;
524
524
  cursor: pointer;
525
525
  }
526
+ /* A saved table or board of docs in a doc (DocViewBlock). */
527
+ .prose-issue .doc-view-block {
528
+ margin-block: 0.75em;
529
+ overflow: hidden;
530
+ border: 1px solid var(--color-border);
531
+ border-radius: 0.5rem;
532
+ font-size: 0.875rem;
533
+ }
534
+ .prose-issue .doc-view-block a[href*="/docs/"] {
535
+ border: 0;
536
+ background: none;
537
+ padding: 0;
538
+ white-space: normal;
539
+ }
540
+ .prose-issue .doc-view-block a[href*="/docs/"]::before {
541
+ content: none;
542
+ }
526
543
  /* A doc's sub-pages, listed in its body (SubPages): rows, not chips. */
527
544
  .prose-issue .sub-pages {
528
545
  margin-block: 0.75em;
@@ -902,6 +919,28 @@ body[data-tandem] > :not(:has(.tandem)) {
902
919
  .collaboration-carets__selection {
903
920
  opacity: 0.25;
904
921
  }
922
+
923
+ /* A space's docs as a table (DocTable). */
924
+ .doc-table th {
925
+ padding: 0.5rem 0.75rem;
926
+ border-bottom: 1px solid var(--color-border);
927
+ color: var(--color-muted-foreground);
928
+ font-size: 0.75rem;
929
+ font-weight: 500;
930
+ text-align: left;
931
+ white-space: nowrap;
932
+ }
933
+ .doc-table td {
934
+ max-width: 20rem;
935
+ padding: 0.5rem 0.75rem;
936
+ border-bottom: 1px solid color-mix(in oklab, var(--color-border) 60%, transparent);
937
+ overflow: hidden;
938
+ text-overflow: ellipsis;
939
+ white-space: nowrap;
940
+ }
941
+ .doc-table tbody tr:hover {
942
+ background: color-mix(in oklab, var(--color-muted) 60%, transparent);
943
+ }
905
944
  /*
906
945
  * For a host that renders Tandem's pages: its own Tailwind v4 build imports
907
946
  * this beside @parallelworks/ui's theme and the typography plugin, so Tandem's