@aglyn/shared-ui-jsx 1.0.0-beta.229 → 1.0.0-beta.231
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/package.json +5 -5
- package/src/lib/components/copy-field.component.d.ts +25 -0
- package/src/lib/components/copy-field.component.js +77 -0
- package/src/lib/components/copy-field.component.js.map +1 -0
- package/src/lib/components/download-cost-badge.component.d.ts +37 -0
- package/src/lib/components/download-cost-badge.component.js +89 -0
- package/src/lib/components/download-cost-badge.component.js.map +1 -0
- package/src/lib/components/file-drop-zone.component.d.ts +48 -0
- package/src/lib/components/file-drop-zone.component.js +173 -0
- package/src/lib/components/file-drop-zone.component.js.map +1 -0
- package/src/lib/components/list-query-notices.component.d.ts +1 -18
- package/src/lib/components/list-query-notices.component.js +1 -11
- package/src/lib/components/list-query-notices.component.js.map +1 -1
- package/src/lib/components/searchable-virtual-list.component.d.ts +72 -0
- package/src/lib/components/searchable-virtual-list.component.js +212 -0
- package/src/lib/components/searchable-virtual-list.component.js.map +1 -0
- package/src/lib/components/site-live-notice.component.d.ts +39 -0
- package/src/lib/components/site-live-notice.component.js +95 -0
- package/src/lib/components/site-live-notice.component.js.map +1 -0
- package/src/lib/components/status-chip.component.d.ts +26 -0
- package/src/lib/components/status-chip.component.js +48 -0
- package/src/lib/components/status-chip.component.js.map +1 -0
- package/src/lib/const/list-filter-sentence.d.ts +1 -24
- package/src/lib/const/list-filter-sentence.js +3 -105
- package/src/lib/const/list-filter-sentence.js.map +1 -1
- package/src/lib/const/list-filter.d.ts +1 -219
- package/src/lib/const/list-filter.js +4 -282
- package/src/lib/const/list-filter.js.map +1 -1
- package/src/lib/const/list-grid-filter.d.ts +4 -25
- package/src/lib/const/list-grid-filter.js +20 -44
- package/src/lib/const/list-grid-filter.js.map +1 -1
- package/src/lib/const/list-query-plan.d.ts +1 -154
- package/src/lib/const/list-query-plan.js +4 -610
- package/src/lib/const/list-query-plan.js.map +1 -1
- package/src/lib/hooks/use-client-pagination.d.ts +25 -0
- package/src/lib/hooks/use-client-pagination.js +65 -0
- package/src/lib/hooks/use-client-pagination.js.map +1 -0
- package/src/lib/hooks/use-in-view.d.ts +16 -0
- package/src/lib/hooks/use-in-view.js +66 -0
- package/src/lib/hooks/use-in-view.js.map +1 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/** One choice of a list's filter chips. */
|
|
3
|
+
export interface SearchableVirtualListFilterOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SearchableVirtualListProps<T> {
|
|
8
|
+
/** The rows to show, already searched and filtered by the caller. */
|
|
9
|
+
items: readonly T[];
|
|
10
|
+
itemKey: (item: T, index: number) => string;
|
|
11
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
12
|
+
/** The search box; the caller decides what a word matches. */
|
|
13
|
+
search: {
|
|
14
|
+
value: string;
|
|
15
|
+
onChange: (value: string) => void;
|
|
16
|
+
label: string;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* One row of chips that narrows the list to one choice, with an "All" chip
|
|
21
|
+
* first. Absent, the list has no chips.
|
|
22
|
+
*/
|
|
23
|
+
filters?: {
|
|
24
|
+
label: string;
|
|
25
|
+
options: readonly SearchableVirtualListFilterOption[];
|
|
26
|
+
/** The chosen option's value, or `''` for all. */
|
|
27
|
+
value: string;
|
|
28
|
+
onChange: (value: string) => void;
|
|
29
|
+
allLabel?: string;
|
|
30
|
+
};
|
|
31
|
+
/** Scrolls with the rows, above them: a pinned section, a heading. */
|
|
32
|
+
header?: ReactNode;
|
|
33
|
+
/** A line under the controls, such as how many rows match. */
|
|
34
|
+
summary?: ReactNode;
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/** Placeholder rows while loading. */
|
|
37
|
+
loadingRows?: number;
|
|
38
|
+
/** The height of a placeholder row, in px. */
|
|
39
|
+
loadingRowHeight?: number;
|
|
40
|
+
error?: {
|
|
41
|
+
message: string;
|
|
42
|
+
onRetry?: () => void;
|
|
43
|
+
} | null;
|
|
44
|
+
/** What an empty list says; shown when `items` is empty and nothing is loading. */
|
|
45
|
+
empty: {
|
|
46
|
+
label: ReactNode;
|
|
47
|
+
description?: ReactNode;
|
|
48
|
+
action?: ReactNode;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* The list's height. Absent, it fills its parent, which must give it one:
|
|
52
|
+
* a flex column with a height, a dialog's content.
|
|
53
|
+
*/
|
|
54
|
+
height?: number | string;
|
|
55
|
+
/** The gap between rows, in theme spacing units. */
|
|
56
|
+
gap?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A long list a person searches, narrows by a row of chips and scrolls, of
|
|
60
|
+
* which only the rows on screen are drawn (AGL-3656, first for the font
|
|
61
|
+
* picker's 1,950 families): the search box, the chips, the list, and its
|
|
62
|
+
* loading, failed and empty states, in the console's own components.
|
|
63
|
+
*
|
|
64
|
+
* The caller owns the words and the chosen chip and hands in the rows they
|
|
65
|
+
* leave, so the list never decides what a search matches. NOT in the barrel:
|
|
66
|
+
* subpath-import it (`@aglyn/shared-ui-jsx/components/searchable-virtual-list.component`).
|
|
67
|
+
*/
|
|
68
|
+
export declare function SearchableVirtualList<T>(props: SearchableVirtualListProps<T>): import("react").JSX.Element;
|
|
69
|
+
export declare namespace SearchableVirtualList {
|
|
70
|
+
var displayName: string;
|
|
71
|
+
}
|
|
72
|
+
export default SearchableVirtualList;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
|
+
import { mdiClose, mdiMagnify } from "@aglyn/shared-data-mdi";
|
|
19
|
+
import { Alert, Box, Button, Chip, IconButton, InputAdornment, Skeleton, Stack, TextField, Typography } from "@mui/material";
|
|
20
|
+
import { useCallback } from "react";
|
|
21
|
+
import { Virtuoso } from "react-virtuoso";
|
|
22
|
+
import EmptyState from "./empty-state.component.js";
|
|
23
|
+
import MdiIcon from "./mdi-icon/mdi-icon.js";
|
|
24
|
+
/**
|
|
25
|
+
* A long list a person searches, narrows by a row of chips and scrolls, of
|
|
26
|
+
* which only the rows on screen are drawn (AGL-3656, first for the font
|
|
27
|
+
* picker's 1,950 families): the search box, the chips, the list, and its
|
|
28
|
+
* loading, failed and empty states, in the console's own components.
|
|
29
|
+
*
|
|
30
|
+
* The caller owns the words and the chosen chip and hands in the rows they
|
|
31
|
+
* leave, so the list never decides what a search matches. NOT in the barrel:
|
|
32
|
+
* subpath-import it (`@aglyn/shared-ui-jsx/components/searchable-virtual-list.component`).
|
|
33
|
+
*/ export function SearchableVirtualList(props) {
|
|
34
|
+
var _filters_allLabel;
|
|
35
|
+
const { items, itemKey, renderItem, search, filters, header, summary, loading = false, loadingRows = 6, loadingRowHeight = 96, error = null, empty, height, gap = 1 } = props;
|
|
36
|
+
const computeItemKey = useCallback((index)=>itemKey(items[index], index), [
|
|
37
|
+
itemKey,
|
|
38
|
+
items
|
|
39
|
+
]);
|
|
40
|
+
const itemContent = useCallback((index)=>/*#__PURE__*/ _jsx(Box, {
|
|
41
|
+
sx: {
|
|
42
|
+
pb: gap
|
|
43
|
+
},
|
|
44
|
+
children: renderItem(items[index], index)
|
|
45
|
+
}), [
|
|
46
|
+
gap,
|
|
47
|
+
items,
|
|
48
|
+
renderItem
|
|
49
|
+
]);
|
|
50
|
+
const Header = useCallback(()=>/*#__PURE__*/ _jsx(_Fragment, {
|
|
51
|
+
children: header != null ? header : null
|
|
52
|
+
}), [
|
|
53
|
+
header
|
|
54
|
+
]);
|
|
55
|
+
let body;
|
|
56
|
+
if (error) {
|
|
57
|
+
body = /*#__PURE__*/ _jsx(Alert, {
|
|
58
|
+
severity: "error",
|
|
59
|
+
action: error.onRetry ? /*#__PURE__*/ _jsx(Button, {
|
|
60
|
+
color: "inherit",
|
|
61
|
+
size: "small",
|
|
62
|
+
onClick: error.onRetry,
|
|
63
|
+
children: 'Try again'
|
|
64
|
+
}) : undefined,
|
|
65
|
+
children: error.message
|
|
66
|
+
});
|
|
67
|
+
} else if (loading) {
|
|
68
|
+
body = /*#__PURE__*/ _jsx(Stack, {
|
|
69
|
+
spacing: gap,
|
|
70
|
+
"aria-busy": true,
|
|
71
|
+
"aria-label": "Loading",
|
|
72
|
+
children: Array.from({
|
|
73
|
+
length: loadingRows
|
|
74
|
+
}, (_, index)=>/*#__PURE__*/ _jsx(Skeleton, {
|
|
75
|
+
variant: "rounded",
|
|
76
|
+
height: loadingRowHeight
|
|
77
|
+
}, index))
|
|
78
|
+
});
|
|
79
|
+
} else if (!items.length) {
|
|
80
|
+
body = /*#__PURE__*/ _jsxs(Box, {
|
|
81
|
+
children: [
|
|
82
|
+
header,
|
|
83
|
+
/*#__PURE__*/ _jsx(EmptyState, {
|
|
84
|
+
compact: true,
|
|
85
|
+
label: empty.label,
|
|
86
|
+
description: empty.description,
|
|
87
|
+
action: empty.action
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
body = /*#__PURE__*/ _jsx(Virtuoso, {
|
|
93
|
+
// Fills the column it is given, or the height it is told.
|
|
94
|
+
style: height === undefined ? {
|
|
95
|
+
flex: '1 1 auto',
|
|
96
|
+
minHeight: 0
|
|
97
|
+
} : {
|
|
98
|
+
height: '100%'
|
|
99
|
+
},
|
|
100
|
+
totalCount: items.length,
|
|
101
|
+
computeItemKey: computeItemKey,
|
|
102
|
+
itemContent: itemContent,
|
|
103
|
+
components: header ? {
|
|
104
|
+
Header
|
|
105
|
+
} : undefined
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
109
|
+
spacing: 1.5,
|
|
110
|
+
sx: {
|
|
111
|
+
minHeight: 0,
|
|
112
|
+
minWidth: 0,
|
|
113
|
+
width: '100%',
|
|
114
|
+
flex: height === undefined ? '1 1 auto' : undefined
|
|
115
|
+
},
|
|
116
|
+
children: [
|
|
117
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
118
|
+
size: "small",
|
|
119
|
+
fullWidth: true,
|
|
120
|
+
// A text box and not `type="search"`: the browser's own clear
|
|
121
|
+
// control would sit beside this one.
|
|
122
|
+
type: "text",
|
|
123
|
+
label: search.label,
|
|
124
|
+
placeholder: search.placeholder,
|
|
125
|
+
value: search.value,
|
|
126
|
+
onChange: (event)=>search.onChange(event.target.value),
|
|
127
|
+
slotProps: {
|
|
128
|
+
htmlInput: {
|
|
129
|
+
inputMode: 'search',
|
|
130
|
+
enterKeyHint: 'search'
|
|
131
|
+
},
|
|
132
|
+
input: {
|
|
133
|
+
startAdornment: /*#__PURE__*/ _jsx(InputAdornment, {
|
|
134
|
+
position: "start",
|
|
135
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
136
|
+
path: mdiMagnify.path
|
|
137
|
+
})
|
|
138
|
+
}),
|
|
139
|
+
endAdornment: search.value ? /*#__PURE__*/ _jsx(InputAdornment, {
|
|
140
|
+
position: "end",
|
|
141
|
+
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
142
|
+
size: "small",
|
|
143
|
+
edge: "end",
|
|
144
|
+
"aria-label": "Clear search",
|
|
145
|
+
onClick: ()=>search.onChange(''),
|
|
146
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
147
|
+
path: mdiClose.path
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
}) : undefined
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}),
|
|
154
|
+
filters ? /*#__PURE__*/ _jsx(Stack, {
|
|
155
|
+
direction: "row",
|
|
156
|
+
spacing: 1,
|
|
157
|
+
role: "radiogroup",
|
|
158
|
+
"aria-label": filters.label,
|
|
159
|
+
sx: {
|
|
160
|
+
// One row that scrolls sideways on a phone rather than wrapping
|
|
161
|
+
// into a block of chips above a short list.
|
|
162
|
+
overflowX: 'auto',
|
|
163
|
+
flexWrap: 'nowrap',
|
|
164
|
+
pb: 0.5,
|
|
165
|
+
scrollbarWidth: 'none',
|
|
166
|
+
'& > *': {
|
|
167
|
+
flexShrink: 0
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
children: [
|
|
171
|
+
{
|
|
172
|
+
value: '',
|
|
173
|
+
label: (_filters_allLabel = filters.allLabel) != null ? _filters_allLabel : 'All'
|
|
174
|
+
},
|
|
175
|
+
...filters.options
|
|
176
|
+
].map((option)=>{
|
|
177
|
+
const selected = filters.value === option.value;
|
|
178
|
+
return /*#__PURE__*/ _jsx(Chip, {
|
|
179
|
+
role: "radio",
|
|
180
|
+
"aria-checked": selected,
|
|
181
|
+
"aria-label": option.label,
|
|
182
|
+
label: option.label,
|
|
183
|
+
size: "small",
|
|
184
|
+
color: selected ? 'primary' : 'default',
|
|
185
|
+
variant: selected ? 'filled' : 'outlined',
|
|
186
|
+
onClick: ()=>filters.onChange(option.value)
|
|
187
|
+
}, option.value || '__all');
|
|
188
|
+
})
|
|
189
|
+
}) : null,
|
|
190
|
+
summary ? /*#__PURE__*/ _jsx(Typography, {
|
|
191
|
+
variant: "caption",
|
|
192
|
+
color: "text.secondary",
|
|
193
|
+
component: "div",
|
|
194
|
+
children: summary
|
|
195
|
+
}) : null,
|
|
196
|
+
/*#__PURE__*/ _jsx(Box, {
|
|
197
|
+
sx: {
|
|
198
|
+
minHeight: 0,
|
|
199
|
+
flex: height === undefined ? '1 1 auto' : undefined,
|
|
200
|
+
height: height === undefined ? undefined : height,
|
|
201
|
+
display: 'flex',
|
|
202
|
+
flexDirection: 'column'
|
|
203
|
+
},
|
|
204
|
+
children: body
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
SearchableVirtualList.displayName = 'SearchableVirtualList';
|
|
210
|
+
export default SearchableVirtualList;
|
|
211
|
+
|
|
212
|
+
//# sourceMappingURL=searchable-virtual-list.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/components/searchable-virtual-list.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { mdiClose, mdiMagnify } from '@aglyn/shared-data-mdi'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n IconButton,\n InputAdornment,\n Skeleton,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { type ReactNode, useCallback } from 'react'\nimport { Virtuoso } from 'react-virtuoso'\nimport EmptyState from './empty-state.component'\nimport MdiIcon from './mdi-icon/mdi-icon'\n\n/** One choice of a list's filter chips. */\nexport interface SearchableVirtualListFilterOption {\n value: string\n label: string\n}\n\nexport interface SearchableVirtualListProps<T> {\n /** The rows to show, already searched and filtered by the caller. */\n items: readonly T[]\n itemKey: (item: T, index: number) => string\n renderItem: (item: T, index: number) => ReactNode\n /** The search box; the caller decides what a word matches. */\n search: {\n value: string\n onChange: (value: string) => void\n label: string\n placeholder?: string\n }\n /**\n * One row of chips that narrows the list to one choice, with an \"All\" chip\n * first. Absent, the list has no chips.\n */\n filters?: {\n label: string\n options: readonly SearchableVirtualListFilterOption[]\n /** The chosen option's value, or `''` for all. */\n value: string\n onChange: (value: string) => void\n allLabel?: string\n }\n /** Scrolls with the rows, above them: a pinned section, a heading. */\n header?: ReactNode\n /** A line under the controls, such as how many rows match. */\n summary?: ReactNode\n loading?: boolean\n /** Placeholder rows while loading. */\n loadingRows?: number\n /** The height of a placeholder row, in px. */\n loadingRowHeight?: number\n error?: { message: string; onRetry?: () => void } | null\n /** What an empty list says; shown when `items` is empty and nothing is loading. */\n empty: { label: ReactNode; description?: ReactNode; action?: ReactNode }\n /**\n * The list's height. Absent, it fills its parent, which must give it one:\n * a flex column with a height, a dialog's content.\n */\n height?: number | string\n /** The gap between rows, in theme spacing units. */\n gap?: number\n}\n\n/**\n * A long list a person searches, narrows by a row of chips and scrolls, of\n * which only the rows on screen are drawn (AGL-3656, first for the font\n * picker's 1,950 families): the search box, the chips, the list, and its\n * loading, failed and empty states, in the console's own components.\n *\n * The caller owns the words and the chosen chip and hands in the rows they\n * leave, so the list never decides what a search matches. NOT in the barrel:\n * subpath-import it (`@aglyn/shared-ui-jsx/components/searchable-virtual-list.component`).\n */\nexport function SearchableVirtualList<T>(props: SearchableVirtualListProps<T>) {\n const {\n items,\n itemKey,\n renderItem,\n search,\n filters,\n header,\n summary,\n loading = false,\n loadingRows = 6,\n loadingRowHeight = 96,\n error = null,\n empty,\n height,\n gap = 1,\n } = props\n const computeItemKey = useCallback(\n (index: number) => itemKey(items[index] as T, index),\n [itemKey, items],\n )\n const itemContent = useCallback(\n (index: number) => (\n <Box sx={{ pb: gap }}>{renderItem(items[index] as T, index)}</Box>\n ),\n [gap, items, renderItem],\n )\n const Header = useCallback(() => <>{header ?? null}</>, [header])\n\n let body: ReactNode\n if (error) {\n body = (\n <Alert\n severity=\"error\"\n action={\n error.onRetry ? (\n <Button color=\"inherit\" size=\"small\" onClick={error.onRetry}>\n {'Try again'}\n </Button>\n ) : undefined\n }\n >\n {error.message}\n </Alert>\n )\n } else if (loading) {\n body = (\n <Stack spacing={gap} aria-busy aria-label=\"Loading\">\n {Array.from({ length: loadingRows }, (_, index) => (\n <Skeleton key={index} variant=\"rounded\" height={loadingRowHeight} />\n ))}\n </Stack>\n )\n } else if (!items.length) {\n body = (\n <Box>\n {header}\n <EmptyState compact label={empty.label} description={empty.description} action={empty.action} />\n </Box>\n )\n } else {\n body = (\n <Virtuoso\n // Fills the column it is given, or the height it is told.\n style={height === undefined ? { flex: '1 1 auto', minHeight: 0 } : { height: '100%' }}\n totalCount={items.length}\n computeItemKey={computeItemKey}\n itemContent={itemContent}\n components={header ? { Header } : undefined}\n />\n )\n }\n\n return (\n <Stack\n spacing={1.5}\n sx={{ minHeight: 0, minWidth: 0, width: '100%', flex: height === undefined ? '1 1 auto' : undefined }}\n >\n <TextField\n size=\"small\"\n fullWidth\n // A text box and not `type=\"search\"`: the browser's own clear\n // control would sit beside this one.\n type=\"text\"\n label={search.label}\n placeholder={search.placeholder}\n value={search.value}\n onChange={(event) => search.onChange(event.target.value)}\n slotProps={{\n htmlInput: { inputMode: 'search', enterKeyHint: 'search' },\n input: {\n startAdornment: (\n <InputAdornment position=\"start\">\n <MdiIcon path={mdiMagnify.path} />\n </InputAdornment>\n ),\n endAdornment: search.value ? (\n <InputAdornment position=\"end\">\n <IconButton\n size=\"small\"\n edge=\"end\"\n aria-label=\"Clear search\"\n onClick={() => search.onChange('')}\n >\n <MdiIcon path={mdiClose.path} />\n </IconButton>\n </InputAdornment>\n ) : undefined,\n },\n }}\n />\n {filters ? (\n <Stack\n direction=\"row\"\n spacing={1}\n role=\"radiogroup\"\n aria-label={filters.label}\n sx={{\n // One row that scrolls sideways on a phone rather than wrapping\n // into a block of chips above a short list.\n overflowX: 'auto',\n flexWrap: 'nowrap',\n pb: 0.5,\n scrollbarWidth: 'none',\n '& > *': { flexShrink: 0 },\n }}\n >\n {[{ value: '', label: filters.allLabel ?? 'All' }, ...filters.options].map((option) => {\n const selected = filters.value === option.value\n return (\n <Chip\n key={option.value || '__all'}\n role=\"radio\"\n aria-checked={selected}\n aria-label={option.label}\n label={option.label}\n size=\"small\"\n color={selected ? 'primary' : 'default'}\n variant={selected ? 'filled' : 'outlined'}\n onClick={() => filters.onChange(option.value)}\n />\n )\n })}\n </Stack>\n ) : null}\n {summary ? (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\">\n {summary}\n </Typography>\n ) : null}\n <Box\n sx={{\n minHeight: 0,\n flex: height === undefined ? '1 1 auto' : undefined,\n height: height === undefined ? undefined : height,\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n {body}\n </Box>\n </Stack>\n )\n}\nSearchableVirtualList.displayName = 'SearchableVirtualList'\n\nexport default SearchableVirtualList\n"],"names":["mdiClose","mdiMagnify","Alert","Box","Button","Chip","IconButton","InputAdornment","Skeleton","Stack","TextField","Typography","useCallback","Virtuoso","EmptyState","MdiIcon","SearchableVirtualList","props","filters","items","itemKey","renderItem","search","header","summary","loading","loadingRows","loadingRowHeight","error","empty","height","gap","computeItemKey","index","itemContent","sx","pb","Header","body","severity","action","onRetry","color","size","onClick","undefined","message","spacing","aria-busy","aria-label","Array","from","length","_","variant","compact","label","description","style","flex","minHeight","totalCount","components","minWidth","width","fullWidth","type","placeholder","value","onChange","event","target","slotProps","htmlInput","inputMode","enterKeyHint","input","startAdornment","position","path","endAdornment","edge","direction","role","overflowX","flexWrap","scrollbarWidth","flexShrink","allLabel","options","map","option","selected","aria-checked","component","display","flexDirection","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SITE_LIVE_NOTICE_TITLE = "Your site is live";
|
|
18
|
+
export declare const SITE_LIVE_NOTICE_BODY = "Your pages are published, and anyone can visit your site now.";
|
|
19
|
+
export interface SiteLiveNoticeProps {
|
|
20
|
+
/** The site's public address; without one the notice says it is live and offers only the pages. */
|
|
21
|
+
liveUrl: string | null;
|
|
22
|
+
/** The console's page list for the site, where the pages are edited. */
|
|
23
|
+
pagesHref: string | null;
|
|
24
|
+
/** The heading's level, for the page or dialog it sits in. */
|
|
25
|
+
headingComponent?: 'h1' | 'h2' | 'h3';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What a person sees the moment their site goes live: that it is live, the
|
|
29
|
+
* address visitors use, and the two things to do next — look at it, or edit
|
|
30
|
+
* its pages. Shared by every path that publishes a new site (the starter, the
|
|
31
|
+
* guided AI start) so a site goes live in the same words whichever way it
|
|
32
|
+
* was made.
|
|
33
|
+
*
|
|
34
|
+
* It exists because nothing said so. A starter site is published the moment
|
|
35
|
+
* it is chosen, and a person who was never told read their analytics window
|
|
36
|
+
* ("14 days") as a countdown to launch.
|
|
37
|
+
*/
|
|
38
|
+
export declare function SiteLiveNotice({ liveUrl, pagesHref, headingComponent }: SiteLiveNoticeProps): import("react").JSX.Element;
|
|
39
|
+
export default SiteLiveNotice;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import Button from "@mui/material/Button";
|
|
19
|
+
import Link from "@mui/material/Link";
|
|
20
|
+
import Stack from "@mui/material/Stack";
|
|
21
|
+
import Typography from "@mui/material/Typography";
|
|
22
|
+
import AppLink from "./app-link.js";
|
|
23
|
+
export const SITE_LIVE_NOTICE_TITLE = 'Your site is live';
|
|
24
|
+
export const SITE_LIVE_NOTICE_BODY = 'Your pages are published, and anyone can visit your site now.';
|
|
25
|
+
/**
|
|
26
|
+
* What a person sees the moment their site goes live: that it is live, the
|
|
27
|
+
* address visitors use, and the two things to do next — look at it, or edit
|
|
28
|
+
* its pages. Shared by every path that publishes a new site (the starter, the
|
|
29
|
+
* guided AI start) so a site goes live in the same words whichever way it
|
|
30
|
+
* was made.
|
|
31
|
+
*
|
|
32
|
+
* It exists because nothing said so. A starter site is published the moment
|
|
33
|
+
* it is chosen, and a person who was never told read their analytics window
|
|
34
|
+
* ("14 days") as a countdown to launch.
|
|
35
|
+
*/ export function SiteLiveNotice({ liveUrl, pagesHref, headingComponent = 'h2' }) {
|
|
36
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
37
|
+
spacing: 2,
|
|
38
|
+
children: [
|
|
39
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
40
|
+
spacing: 0.5,
|
|
41
|
+
children: [
|
|
42
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
43
|
+
variant: "h5",
|
|
44
|
+
component: headingComponent,
|
|
45
|
+
children: SITE_LIVE_NOTICE_TITLE
|
|
46
|
+
}),
|
|
47
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
48
|
+
variant: "body1",
|
|
49
|
+
color: "text.secondary",
|
|
50
|
+
children: SITE_LIVE_NOTICE_BODY
|
|
51
|
+
}),
|
|
52
|
+
liveUrl ? /*#__PURE__*/ _jsx(Link, {
|
|
53
|
+
href: liveUrl,
|
|
54
|
+
target: "_blank",
|
|
55
|
+
rel: "noopener noreferrer",
|
|
56
|
+
variant: "body1",
|
|
57
|
+
sx: {
|
|
58
|
+
wordBreak: 'break-all'
|
|
59
|
+
},
|
|
60
|
+
children: liveUrl.replace(/^https?:\/\//, '')
|
|
61
|
+
}) : null
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
65
|
+
direction: {
|
|
66
|
+
xs: 'column',
|
|
67
|
+
sm: 'row'
|
|
68
|
+
},
|
|
69
|
+
spacing: 2,
|
|
70
|
+
children: [
|
|
71
|
+
liveUrl ? // The live site, in a tab of its own: the console stays where it is.
|
|
72
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
73
|
+
variant: "contained",
|
|
74
|
+
size: "large",
|
|
75
|
+
component: "a",
|
|
76
|
+
href: liveUrl,
|
|
77
|
+
target: "_blank",
|
|
78
|
+
rel: "noopener noreferrer",
|
|
79
|
+
children: 'View your site'
|
|
80
|
+
}) : null,
|
|
81
|
+
pagesHref ? /*#__PURE__*/ _jsx(Button, {
|
|
82
|
+
variant: liveUrl ? 'outlined' : 'contained',
|
|
83
|
+
size: "large",
|
|
84
|
+
component: AppLink,
|
|
85
|
+
href: pagesHref,
|
|
86
|
+
children: 'Edit your pages'
|
|
87
|
+
}) : null
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
export default SiteLiveNotice;
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=site-live-notice.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/components/site-live-notice.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport Button from '@mui/material/Button'\nimport Link from '@mui/material/Link'\nimport Stack from '@mui/material/Stack'\nimport Typography from '@mui/material/Typography'\nimport AppLink from './app-link'\n\nexport const SITE_LIVE_NOTICE_TITLE = 'Your site is live'\nexport const SITE_LIVE_NOTICE_BODY = 'Your pages are published, and anyone can visit your site now.'\n\nexport interface SiteLiveNoticeProps {\n /** The site's public address; without one the notice says it is live and offers only the pages. */\n liveUrl: string | null\n /** The console's page list for the site, where the pages are edited. */\n pagesHref: string | null\n /** The heading's level, for the page or dialog it sits in. */\n headingComponent?: 'h1' | 'h2' | 'h3'\n}\n\n/**\n * What a person sees the moment their site goes live: that it is live, the\n * address visitors use, and the two things to do next — look at it, or edit\n * its pages. Shared by every path that publishes a new site (the starter, the\n * guided AI start) so a site goes live in the same words whichever way it\n * was made.\n *\n * It exists because nothing said so. A starter site is published the moment\n * it is chosen, and a person who was never told read their analytics window\n * (\"14 days\") as a countdown to launch.\n */\nexport function SiteLiveNotice({ liveUrl, pagesHref, headingComponent = 'h2' }: SiteLiveNoticeProps) {\n return (\n <Stack spacing={2}>\n <Stack spacing={0.5}>\n <Typography variant=\"h5\" component={headingComponent}>\n {SITE_LIVE_NOTICE_TITLE}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {SITE_LIVE_NOTICE_BODY}\n </Typography>\n {liveUrl ? (\n <Link href={liveUrl} target=\"_blank\" rel=\"noopener noreferrer\" variant=\"body1\" sx={{ wordBreak: 'break-all' }}>\n {liveUrl.replace(/^https?:\\/\\//, '')}\n </Link>\n ) : null}\n </Stack>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n {liveUrl ? (\n // The live site, in a tab of its own: the console stays where it is.\n <Button variant=\"contained\" size=\"large\" component=\"a\" href={liveUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n {'View your site'}\n </Button>\n ) : null}\n {pagesHref ? (\n <Button variant={liveUrl ? 'outlined' : 'contained'} size=\"large\" component={AppLink} href={pagesHref}>\n {'Edit your pages'}\n </Button>\n ) : null}\n </Stack>\n </Stack>\n )\n}\n\nexport default SiteLiveNotice\n"],"names":["Button","Link","Stack","Typography","AppLink","SITE_LIVE_NOTICE_TITLE","SITE_LIVE_NOTICE_BODY","SiteLiveNotice","liveUrl","pagesHref","headingComponent","spacing","variant","component","color","href","target","rel","sx","wordBreak","replace","direction","xs","sm","size"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,OAAOA,YAAY,uBAAsB;AACzC,OAAOC,UAAU,qBAAoB;AACrC,OAAOC,WAAW,sBAAqB;AACvC,OAAOC,gBAAgB,2BAA0B;AACjD,OAAOC,aAAa,gBAAY;AAEhC,OAAO,MAAMC,yBAAyB,oBAAmB;AACzD,OAAO,MAAMC,wBAAwB,gEAA+D;AAWpG;;;;;;;;;;CAUC,GACD,OAAO,SAASC,eAAe,EAAEC,OAAO,EAAEC,SAAS,EAAEC,mBAAmB,IAAI,EAAuB;IACjG,qBACE,MAACR;QAAMS,SAAS;;0BACd,MAACT;gBAAMS,SAAS;;kCACd,KAACR;wBAAWS,SAAQ;wBAAKC,WAAWH;kCACjCL;;kCAEH,KAACF;wBAAWS,SAAQ;wBAAQE,OAAM;kCAC/BR;;oBAEFE,wBACC,KAACP;wBAAKc,MAAMP;wBAASQ,QAAO;wBAASC,KAAI;wBAAsBL,SAAQ;wBAAQM,IAAI;4BAAEC,WAAW;wBAAY;kCACzGX,QAAQY,OAAO,CAAC,gBAAgB;yBAEjC;;;0BAEN,MAAClB;gBAAMmB,WAAW;oBAAEC,IAAI;oBAAUC,IAAI;gBAAM;gBAAGZ,SAAS;;oBACrDH,UACC,qEAAqE;kCACrE,KAACR;wBAAOY,SAAQ;wBAAYY,MAAK;wBAAQX,WAAU;wBAAIE,MAAMP;wBAASQ,QAAO;wBAASC,KAAI;kCACvF;yBAED;oBACHR,0BACC,KAACT;wBAAOY,SAASJ,UAAU,aAAa;wBAAagB,MAAK;wBAAQX,WAAWT;wBAASW,MAAMN;kCACzF;yBAED;;;;;AAIZ;AAEA,eAAeF,eAAc"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* What a status says about the thing it labels. The five tones are the
|
|
4
|
+
* theme's own palette roles, so a status reads the same in every card.
|
|
5
|
+
*/
|
|
6
|
+
export type StatusTone = 'neutral' | 'info' | 'success' | 'warning' | 'error';
|
|
7
|
+
export interface StatusChipProps {
|
|
8
|
+
label: string;
|
|
9
|
+
tone?: StatusTone;
|
|
10
|
+
/** An icon before the label, such as an `MdiIcon`. */
|
|
11
|
+
icon?: ReactElement;
|
|
12
|
+
/** Outlined for a quiet status beside other content; filled to stand out. */
|
|
13
|
+
variant?: 'filled' | 'outlined';
|
|
14
|
+
'data-testid'?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A record's state as a small chip — On, Connected, Needs attention
|
|
18
|
+
* (AGL-3637). Plugins mapped each status to a hand-picked MUI `Chip` color
|
|
19
|
+
* per card, and the same state came out in different colors on different
|
|
20
|
+
* cards; a tone names what the state means and the theme picks the color.
|
|
21
|
+
*/
|
|
22
|
+
export declare function StatusChip(props: StatusChipProps): import("react").JSX.Element;
|
|
23
|
+
export declare namespace StatusChip {
|
|
24
|
+
var displayName: string;
|
|
25
|
+
}
|
|
26
|
+
export default StatusChip;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
19
|
+
import Chip from "@mui/material/Chip";
|
|
20
|
+
const TONE_COLOR = {
|
|
21
|
+
neutral: 'default',
|
|
22
|
+
info: 'info',
|
|
23
|
+
success: 'success',
|
|
24
|
+
warning: 'warning',
|
|
25
|
+
error: 'error'
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A record's state as a small chip — On, Connected, Needs attention
|
|
29
|
+
* (AGL-3637). Plugins mapped each status to a hand-picked MUI `Chip` color
|
|
30
|
+
* per card, and the same state came out in different colors on different
|
|
31
|
+
* cards; a tone names what the state means and the theme picks the color.
|
|
32
|
+
*/ export function StatusChip(props) {
|
|
33
|
+
const { label, tone = 'neutral', icon, variant = 'filled' } = props;
|
|
34
|
+
return /*#__PURE__*/ _jsx(Chip, _extends({
|
|
35
|
+
size: "small",
|
|
36
|
+
label: label,
|
|
37
|
+
color: TONE_COLOR[tone],
|
|
38
|
+
variant: variant
|
|
39
|
+
}, icon ? {
|
|
40
|
+
icon
|
|
41
|
+
} : {}, {
|
|
42
|
+
"data-testid": props['data-testid']
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
StatusChip.displayName = 'StatusChip';
|
|
46
|
+
export default StatusChip;
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=status-chip.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/components/status-chip.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport Chip, { type ChipProps } from '@mui/material/Chip'\nimport type { ReactElement } from 'react'\n\n/**\n * What a status says about the thing it labels. The five tones are the\n * theme's own palette roles, so a status reads the same in every card.\n */\nexport type StatusTone = 'neutral' | 'info' | 'success' | 'warning' | 'error'\n\nconst TONE_COLOR: Readonly<Record<StatusTone, ChipProps['color']>> = {\n neutral: 'default',\n info: 'info',\n success: 'success',\n warning: 'warning',\n error: 'error',\n}\n\nexport interface StatusChipProps {\n label: string\n tone?: StatusTone\n /** An icon before the label, such as an `MdiIcon`. */\n icon?: ReactElement\n /** Outlined for a quiet status beside other content; filled to stand out. */\n variant?: 'filled' | 'outlined'\n 'data-testid'?: string\n}\n\n/**\n * A record's state as a small chip — On, Connected, Needs attention\n * (AGL-3637). Plugins mapped each status to a hand-picked MUI `Chip` color\n * per card, and the same state came out in different colors on different\n * cards; a tone names what the state means and the theme picks the color.\n */\nexport function StatusChip(props: StatusChipProps) {\n const { label, tone = 'neutral', icon, variant = 'filled' } = props\n return (\n <Chip\n size=\"small\"\n label={label}\n color={TONE_COLOR[tone]}\n variant={variant}\n {...(icon ? { icon } : {})}\n data-testid={props['data-testid']}\n />\n )\n}\nStatusChip.displayName = 'StatusChip'\n\nexport default StatusChip\n"],"names":["Chip","TONE_COLOR","neutral","info","success","warning","error","StatusChip","props","label","tone","icon","variant","size","color","data-testid","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,OAAOA,UAA8B,qBAAoB;AASzD,MAAMC,aAA+D;IACnEC,SAAS;IACTC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,OAAO;AACT;AAYA;;;;;CAKC,GACD,OAAO,SAASC,WAAWC,KAAsB;IAC/C,MAAM,EAAEC,KAAK,EAAEC,OAAO,SAAS,EAAEC,IAAI,EAAEC,UAAU,QAAQ,EAAE,GAAGJ;IAC9D,qBACE,KAACR;QACCa,MAAK;QACLJ,OAAOA;QACPK,OAAOb,UAAU,CAACS,KAAK;QACvBE,SAASA;OACJD,OAAO;QAAEA;IAAK,IAAI,CAAC;QACxBI,eAAaP,KAAK,CAAC,cAAc;;AAGvC;AACAD,WAAWS,WAAW,GAAG;AAEzB,eAAeT,WAAU"}
|
|
@@ -14,27 +14,4 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
|
|
18
|
-
import type { ListFilterClause, ListFilterOption } from './list-grid-filter';
|
|
19
|
-
/**
|
|
20
|
-
* How an operator reads on a chip or in a sentence — "Owner is Dana",
|
|
21
|
-
* "Created on or after 1 Jan". One map so every list that shows its
|
|
22
|
-
* clauses back spells them the same way; an operator nothing here names
|
|
23
|
-
* is shown as itself rather than hidden.
|
|
24
|
-
*/
|
|
25
|
-
export declare function listFilterOperatorLabel(op: string): string;
|
|
26
|
-
export declare const listFilterDay: (raw: string) => Date;
|
|
27
|
-
/**
|
|
28
|
-
* How a clause reads as a sentence — "Owner is Dana", "Created on or after
|
|
29
|
-
* 1 Jan", "Reason is any of Bounced, Complained": its header, its operator,
|
|
30
|
-
* and its value by the label of the option it names.
|
|
31
|
-
*
|
|
32
|
-
* ONE wording, used by the chips over a list and by the notices that say a
|
|
33
|
-
* clause was not applied (`listQueryRefusals`), so the two cannot describe
|
|
34
|
-
* the same clause differently.
|
|
35
|
-
*/
|
|
36
|
-
export declare function listFilterClauseSentence(clause: ListFilterClause, context: {
|
|
37
|
-
fields: readonly ListFilterField[];
|
|
38
|
-
headers?: Readonly<Record<string, string>>;
|
|
39
|
-
options?: Readonly<Record<string, readonly ListFilterOption[]>>;
|
|
40
|
-
}): string;
|
|
17
|
+
export * from '@aglyn/shared-util-tools/list-query/list-filter-sentence';
|