@stapel/listings-react 0.4.0 → 0.7.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.
- package/CHANGELOG.md +102 -0
- package/dist/api/generated/schema.d.ts +2 -2
- package/dist/api/generated/schema.d.ts.map +1 -1
- package/dist/default/FavoritesPane.d.ts +40 -3
- package/dist/default/FavoritesPane.d.ts.map +1 -1
- package/dist/default/FavoritesPane.js +30 -18
- package/dist/default/FavoritesPane.js.map +1 -1
- package/dist/default/ListingCard.d.ts +25 -4
- package/dist/default/ListingCard.d.ts.map +1 -1
- package/dist/default/ListingCard.js +19 -12
- package/dist/default/ListingCard.js.map +1 -1
- package/dist/default/ListingComposerPage.d.ts +163 -23
- package/dist/default/ListingComposerPage.d.ts.map +1 -1
- package/dist/default/ListingComposerPage.js +85 -43
- package/dist/default/ListingComposerPage.js.map +1 -1
- package/dist/default/ListingDetailPane.d.ts +41 -4
- package/dist/default/ListingDetailPane.d.ts.map +1 -1
- package/dist/default/ListingDetailPane.js +38 -25
- package/dist/default/ListingDetailPane.js.map +1 -1
- package/dist/default/ListingPhoto.d.ts +13 -1
- package/dist/default/ListingPhoto.d.ts.map +1 -1
- package/dist/default/ListingPhoto.js +47 -9
- package/dist/default/ListingPhoto.js.map +1 -1
- package/dist/default/ListingPrice.d.ts +13 -0
- package/dist/default/ListingPrice.d.ts.map +1 -0
- package/dist/default/ListingPrice.js +60 -0
- package/dist/default/ListingPrice.js.map +1 -0
- package/dist/default/MyListingsPane.d.ts +7 -28
- package/dist/default/MyListingsPane.d.ts.map +1 -1
- package/dist/default/MyListingsPane.js +136 -67
- package/dist/default/MyListingsPane.js.map +1 -1
- package/dist/default/SignInLink.js +8 -7
- package/dist/default/SignInLink.js.map +1 -1
- package/dist/default/StatusTags.d.ts +16 -5
- package/dist/default/StatusTags.d.ts.map +1 -1
- package/dist/default/StatusTags.js +19 -13
- package/dist/default/StatusTags.js.map +1 -1
- package/dist/default/index.d.ts +15 -9
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +13 -6
- package/dist/default/index.js.map +1 -1
- package/dist/headless/Favorites.d.ts +6 -0
- package/dist/headless/Favorites.d.ts.map +1 -1
- package/dist/headless/Favorites.js +4 -0
- package/dist/headless/Favorites.js.map +1 -1
- package/dist/headless/ListingActions.d.ts +12 -0
- package/dist/headless/ListingActions.d.ts.map +1 -1
- package/dist/headless/ListingActions.js +3 -0
- package/dist/headless/ListingActions.js.map +1 -1
- package/dist/headless/ListingComposer.d.ts +23 -0
- package/dist/headless/ListingComposer.d.ts.map +1 -1
- package/dist/headless/ListingComposer.js +33 -7
- package/dist/headless/ListingComposer.js.map +1 -1
- package/dist/i18n/es.d.ts +6 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +21 -4
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/keys.d.ts +19 -2
- package/dist/i18n/keys.d.ts.map +1 -1
- package/dist/i18n/keys.js +35 -5
- package/dist/i18n/keys.js.map +1 -1
- package/dist/i18n/ru.d.ts +16 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +33 -4
- package/dist/i18n/ru.js.map +1 -1
- package/dist/model/draft.d.ts +9 -6
- package/dist/model/draft.d.ts.map +1 -1
- package/dist/model/draft.js +3 -1
- package/dist/model/draft.js.map +1 -1
- package/dist/model/features.d.ts +21 -7
- package/dist/model/features.d.ts.map +1 -1
- package/dist/model/features.js +26 -0
- package/dist/model/features.js.map +1 -1
- package/llms.txt +7 -6
- package/manifest.json +53 -11
- package/nav-manifest.json +1 -1
- package/package.json +17 -12
- package/src/analytics/generated/events.json +1 -1
- package/src/api/generated/schema.ts +2 -2
- package/src/default/FavoritesPane.tsx +156 -82
- package/src/default/ListingCard.tsx +88 -46
- package/src/default/ListingComposerPage.tsx +403 -187
- package/src/default/ListingDetailPane.tsx +200 -98
- package/src/default/ListingPhoto.tsx +89 -16
- package/src/default/ListingPrice.tsx +77 -0
- package/src/default/MyListingsPane.tsx +286 -228
- package/src/default/SignInLink.tsx +4 -4
- package/src/default/StatusTags.tsx +36 -18
- package/src/default/index.ts +18 -7
- package/src/headless/Favorites.tsx +10 -0
- package/src/headless/ListingActions.tsx +19 -0
- package/src/headless/ListingComposer.tsx +56 -5
- package/src/i18n/es.ts +26 -4
- package/src/i18n/keys.ts +42 -5
- package/src/i18n/ru.ts +38 -4
- package/src/model/draft.ts +12 -7
- package/src/model/features.ts +48 -7
- package/dist/default/ErrorAlert.d.ts +0 -9
- package/dist/default/ErrorAlert.d.ts.map +0 -1
- package/dist/default/ErrorAlert.js +0 -27
- package/dist/default/ErrorAlert.js.map +0 -1
- package/dist/default/theme.d.ts +0 -25
- package/dist/default/theme.d.ts.map +0 -1
- package/dist/default/theme.js +0 -40
- package/dist/default/theme.js.map +0 -1
- package/src/default/ErrorAlert.tsx +0 -46
- package/src/default/theme.tsx +0 -67
package/src/i18n/ru.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { I18nDictionary, I18nEngine } from "@stapel/core";
|
|
2
|
+
import { registerAttributesI18nRu } from "@stapel/attributes-react/i18n/ru";
|
|
2
3
|
import { listingsErrorBundleRu } from "./generated/errors.ru.gen.js";
|
|
3
4
|
|
|
4
5
|
export { listingsErrorBundleRu } from "./generated/errors.ru.gen.js";
|
|
@@ -110,6 +111,8 @@ export const listingsI18nBundleRu: I18nDictionary = {
|
|
|
110
111
|
"listings.detail.published_at": "Опубликовано {date}",
|
|
111
112
|
"listings.detail.expires_at": "В продаже до {date}",
|
|
112
113
|
"listings.detail.stock": "В наличии",
|
|
114
|
+
"listings.detail.edit": "Редактировать объявление",
|
|
115
|
+
"listings.detail.take_down": "Снять с публикации",
|
|
113
116
|
|
|
114
117
|
"listings.compose.new_title": "Новое объявление",
|
|
115
118
|
"listings.compose.edit_title": "Редактирование объявления",
|
|
@@ -128,8 +131,8 @@ export const listingsI18nBundleRu: I18nDictionary = {
|
|
|
128
131
|
"Введите цену числом, не более двух знаков после запятой",
|
|
129
132
|
"listings.compose.currency_label": "Валюта",
|
|
130
133
|
"listings.compose.location_label": "Где находится",
|
|
131
|
-
"listings.compose.
|
|
132
|
-
|
|
134
|
+
"listings.compose.location_help":
|
|
135
|
+
"Покупатели ищут по расстоянию — объявление без места они просто не найдут",
|
|
133
136
|
"listings.compose.geo_incomplete":
|
|
134
137
|
"Рядом с широтой нужна долгота — половина координаты никуда не указывает",
|
|
135
138
|
"listings.compose.photos": "Фотографии",
|
|
@@ -160,12 +163,13 @@ export const listingsI18nBundleRu: I18nDictionary = {
|
|
|
160
163
|
"listings.compose.blocked.no_category":
|
|
161
164
|
"Выберите категорию — от неё зависит остальная форма",
|
|
162
165
|
"listings.compose.blocked.unsupported_type":
|
|
163
|
-
"Эта категория спрашивает характеристику, которую приложение пока не умеет
|
|
166
|
+
"Эта категория спрашивает характеристику, которую приложение пока не умеет показывать, поэтому закончить объявление здесь нельзя",
|
|
164
167
|
"listings.compose.blocked.photos_pending":
|
|
165
168
|
"Дождитесь окончания загрузки фотографий",
|
|
166
169
|
"listings.compose.blocked.no_draft": "Черновик ещё не создан",
|
|
167
170
|
"listings.compose.blocked.busy":
|
|
168
171
|
"Секунду — предыдущее изменение ещё сохраняется",
|
|
172
|
+
"listings.compose.blocked.incomplete": "Не заполнено обязательных деталей: {count}",
|
|
169
173
|
"listings.compose.blocked.mirror": "Сначала поправьте отмеченные поля",
|
|
170
174
|
"listings.compose.blocked.details_unavailable":
|
|
171
175
|
"Не удалось загрузить характеристики категории, поэтому проверить форму нельзя",
|
|
@@ -184,6 +188,10 @@ export const listingsI18nBundleRu: I18nDictionary = {
|
|
|
184
188
|
"listings.mine.empty.archived": "Пока ничего не в архиве, не снято, не истекло и не продано",
|
|
185
189
|
"listings.mine.blocked.title":
|
|
186
190
|
"Модерация сняла ваших объявлений: {count}",
|
|
191
|
+
"listings.mine.blocked.title.one": "Модерация сняла одно ваше объявление",
|
|
192
|
+
"listings.mine.blocked.title.few": "Модерация сняла {count} ваших объявления",
|
|
193
|
+
"listings.mine.blocked.title.many": "Модерация сняла {count} ваших объявлений",
|
|
194
|
+
"listings.mine.blocked.title.other": "Модерация сняла ваших объявлений: {count}",
|
|
187
195
|
"listings.mine.blocked.load_failed":
|
|
188
196
|
"Не удалось проверить, снимала ли модерация ваши объявления",
|
|
189
197
|
"listings.mine.live_under_review": "Опубликовано, правки на проверке",
|
|
@@ -191,11 +199,18 @@ export const listingsI18nBundleRu: I18nDictionary = {
|
|
|
191
199
|
"listings.mine.archive": "В архив",
|
|
192
200
|
"listings.mine.complete": "Отметить проданным",
|
|
193
201
|
"listings.mine.delete": "Удалить",
|
|
202
|
+
"listings.mine.delete_confirm_title": "Удалить объявление?",
|
|
203
|
+
"listings.mine.delete_confirm_body":
|
|
204
|
+
"Оно исчезнет из кабинета, и вернуть его будет нельзя. Архив сохраняет его.",
|
|
194
205
|
|
|
195
206
|
"listings.favorites.title": "Избранное",
|
|
196
207
|
"listings.favorites.loading": "Загружаем избранное…",
|
|
197
208
|
"listings.favorites.load_failed": "Не удалось загрузить избранное",
|
|
198
209
|
"listings.favorites.empty": "Вы ещё ничего не сохранили",
|
|
210
|
+
"listings.favorites.empty_hint":
|
|
211
|
+
"Нажмите на сердечко в любом объявлении — оно будет ждать здесь.",
|
|
212
|
+
"listings.favorites.sign_in_hint":
|
|
213
|
+
"Избранное хранится в аккаунте, поэтому переходит с вами на другие устройства.",
|
|
199
214
|
|
|
200
215
|
"listings.blocked.sign_in": "Войдите, чтобы сделать это",
|
|
201
216
|
"listings.blocked.guest":
|
|
@@ -207,9 +222,12 @@ export const listingsI18nBundleRu: I18nDictionary = {
|
|
|
207
222
|
"listings.blocked.delete_active":
|
|
208
223
|
"Сначала уберите в архив — объявление в продаже удалить нельзя",
|
|
209
224
|
"listings.blocked.in_flight": "Секунду — это уже выполняется",
|
|
225
|
+
"listings.blocked.no_editor":
|
|
226
|
+
"В этом приложении пока нет экрана редактирования объявления",
|
|
210
227
|
|
|
211
228
|
"listings.page.prev": "Назад",
|
|
212
229
|
"listings.page.next": "Дальше",
|
|
230
|
+
"listings.page.indicator": "Страница {page}",
|
|
213
231
|
|
|
214
232
|
"listings.nav.detail": "Объявление",
|
|
215
233
|
"listings.nav.compose": "Подать объявление",
|
|
@@ -217,7 +235,23 @@ export const listingsI18nBundleRu: I18nDictionary = {
|
|
|
217
235
|
"listings.nav.favorites": "Избранное",
|
|
218
236
|
};
|
|
219
237
|
|
|
220
|
-
/**
|
|
238
|
+
/**
|
|
239
|
+
* Register the Russian bundle into a core i18n engine — AND the twelve
|
|
240
|
+
* `stapel_attributes` sentences this pair deliberately does not author.
|
|
241
|
+
*
|
|
242
|
+
* The split of ownership is right and stays: two packages must not give one
|
|
243
|
+
* refusal two sentences. What was wrong was leaving the JOIN to a README. A
|
|
244
|
+
* host that registered only this bundle got twelve of the composer's most
|
|
245
|
+
* common refusals ("this value is below the minimum", "the description is too
|
|
246
|
+
* long") in English on a Russian page, and nothing failed anywhere to say so.
|
|
247
|
+
*
|
|
248
|
+
* `@stapel/attributes-react` is a peer of this pair and its editors are what
|
|
249
|
+
* RAISE those twelve, so a listings-ru host is an attributes-ru host by
|
|
250
|
+
* construction; chaining the registration states that instead of asking. It is
|
|
251
|
+
* idempotent — a host that also registers the bundle itself simply writes the
|
|
252
|
+
* same keys twice.
|
|
253
|
+
*/
|
|
221
254
|
export function registerListingsI18nRu(i18n: I18nEngine): void {
|
|
255
|
+
registerAttributesI18nRu(i18n);
|
|
222
256
|
i18n.registerBundle("ru", listingsI18nBundleRu);
|
|
223
257
|
}
|
package/src/model/draft.ts
CHANGED
|
@@ -29,12 +29,14 @@ import { DEFAULT_LISTING_CURRENCY } from "../api/types.js";
|
|
|
29
29
|
* `geohash` that disagrees with the coordinates beside it is worse than no
|
|
30
30
|
* geohash — the pin and the bucket point at different places.
|
|
31
31
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
32
|
+
* `geohash` is SERVER-COMPUTED and read-only since stapel-listings 0.7.1.
|
|
33
|
+
* `Listing.save()` stamps `geohash_draft` from `lat_draft`/`lon_draft` through
|
|
34
|
+
* the `geo.geohash_encode` comm function, and `ListingDraftSerializer` marks
|
|
35
|
+
* the field `readOnly` — a value sent in the request body is silently ignored.
|
|
36
|
+
* So this member is something the composer READS BACK (a reopened draft, a
|
|
37
|
+
* published listing) and never something it sends: `draftPatchFromValues`
|
|
38
|
+
* omits it, and a picker that happens to know a geohash may keep it for its
|
|
39
|
+
* own display without expecting the wire to carry it.
|
|
38
40
|
*/
|
|
39
41
|
export interface ListingLocation {
|
|
40
42
|
/** Opaque id from whatever place directory the deployment uses. */
|
|
@@ -45,6 +47,7 @@ export interface ListingLocation {
|
|
|
45
47
|
* `55.796100` into `55.7961` and change what was submitted. */
|
|
46
48
|
readonly lat: string | null;
|
|
47
49
|
readonly lon: string | null;
|
|
50
|
+
/** Read-only: the server computes it from `lat`/`lon` (0.7.1). Never sent. */
|
|
48
51
|
readonly geohash: string;
|
|
49
52
|
}
|
|
50
53
|
|
|
@@ -242,7 +245,9 @@ export function draftPatchFromValues(
|
|
|
242
245
|
images_draft: [...values.images],
|
|
243
246
|
location_id_draft: values.location.locationId,
|
|
244
247
|
location_label_draft: values.location.locationLabel,
|
|
245
|
-
geohash_draft
|
|
248
|
+
// No `geohash_draft`: stapel-listings 0.7.1 computes it in `Listing.save()`
|
|
249
|
+
// from the coordinates and marks the serializer field read-only, so a value
|
|
250
|
+
// sent here is discarded. Sending one would be a claim the wire ignores.
|
|
246
251
|
lat_draft: values.location.lat,
|
|
247
252
|
lon_draft: values.location.lon,
|
|
248
253
|
features_draft: toWireFeatures(features, values.features),
|
package/src/model/features.ts
CHANGED
|
@@ -22,14 +22,28 @@
|
|
|
22
22
|
* `order`, `title`, `badge`) are simply ignored by every formatter. Copying a
|
|
23
23
|
* hand-picked subset instead would be a list to keep in step with ten types.
|
|
24
24
|
*
|
|
25
|
-
* ── What a DAO does NOT carry
|
|
25
|
+
* ── What a DAO does NOT carry, and the one line that repairs it ────────────
|
|
26
26
|
*
|
|
27
|
-
* `select`'s `options`. `
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `
|
|
27
|
+
* `select`'s `options`. `SelectType.dto_to_dao` stores the chosen VALUES and
|
|
28
|
+
* the ui config, never the option table — the table lives on the CATEGORY, and
|
|
29
|
+
* not needing it is the whole point of the projection.
|
|
30
|
+
*
|
|
31
|
+
* The consequence was a defect the visual pass caught on every card and every
|
|
32
|
+
* spec row: `formatFeatureValue` resolves an option's copy by looking the
|
|
33
|
+
* value up in `config.options`, and with no table it falls through to
|
|
34
|
+
* `String(value)`. The stored value of a translatable catalogue IS a
|
|
35
|
+
* translation key, so a listing page printed `demo.condition.used` and
|
|
36
|
+
* `demo.brand.bosch` at people. (The server does not have this problem: its
|
|
37
|
+
* own `format_value` has the category's config in hand.)
|
|
38
|
+
*
|
|
39
|
+
* So the split below synthesizes the IDENTITY table — `{value: v, label: v}`
|
|
40
|
+
* for each stored value — which is exactly the table a translatable catalogue
|
|
41
|
+
* would have produced, since its labels ARE the keys. `formatFeatureValue`
|
|
42
|
+
* then runs the value through the host's `t` and a bundle carrying the
|
|
43
|
+
* catalogue copy reads "Used". A bundle that does not still shows the key, on
|
|
44
|
+
* purpose: a visible `option.condition.used` gets fixed, an invented "Used"
|
|
45
|
+
* ships wrong. A NON-translatable catalogue stores literal labels, `t` returns
|
|
46
|
+
* an unknown key unchanged, and the output is what it always was.
|
|
33
47
|
*/
|
|
34
48
|
import type { FeatureDef, FeatureValueDto } from "@stapel/attributes-react";
|
|
35
49
|
import type { ListingFeatureDao, ListingFeatureView } from "../api/types.js";
|
|
@@ -37,6 +51,31 @@ import type { ListingFeatureDao, ListingFeatureView } from "../api/types.js";
|
|
|
37
51
|
/** Keys that belong to the DAO envelope rather than to the type's config. */
|
|
38
52
|
const ENVELOPE = new Set(["slug", "value", "name", "order", "title", "badge"]);
|
|
39
53
|
|
|
54
|
+
/** The two types whose stored `value` is an option key rather than the thing
|
|
55
|
+
* itself. `hierarchical_select` is deliberately absent: its formatter joins
|
|
56
|
+
* the path with " / " and never consults an option table at all. */
|
|
57
|
+
const OPTION_VALUED = new Set(["select"]);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The identity option table for a stored `select` — see the module header.
|
|
61
|
+
*
|
|
62
|
+
* Returns `undefined` when there is nothing to add (another type, a config
|
|
63
|
+
* that already carries a table, a value that is not a list of strings), so the
|
|
64
|
+
* common path allocates nothing and a DAO that DOES carry options is left
|
|
65
|
+
* exactly as it arrived.
|
|
66
|
+
*/
|
|
67
|
+
function synthesizedOptions(
|
|
68
|
+
dao: ListingFeatureDao,
|
|
69
|
+
config: Readonly<Record<string, unknown>>
|
|
70
|
+
): readonly { value: string; label: string }[] | undefined {
|
|
71
|
+
if (typeof dao.type !== "string" || !OPTION_VALUED.has(dao.type)) return undefined;
|
|
72
|
+
if (Array.isArray(config["options"])) return undefined;
|
|
73
|
+
const raw = Array.isArray(dao.value) ? dao.value : [dao.value];
|
|
74
|
+
const values = raw.filter((item): item is string => typeof item === "string");
|
|
75
|
+
if (values.length === 0) return undefined;
|
|
76
|
+
return values.map((value) => ({ value, label: value }));
|
|
77
|
+
}
|
|
78
|
+
|
|
40
79
|
/**
|
|
41
80
|
* One DAO row → the definition/value pair the display half consumes.
|
|
42
81
|
*
|
|
@@ -54,6 +93,8 @@ export function featureFromDao(
|
|
|
54
93
|
for (const [key, value] of Object.entries(dao)) {
|
|
55
94
|
if (!ENVELOPE.has(key)) config[key] = value;
|
|
56
95
|
}
|
|
96
|
+
const options = synthesizedOptions(dao, config);
|
|
97
|
+
if (options !== undefined) config["options"] = options;
|
|
57
98
|
|
|
58
99
|
const feature: FeatureDef = {
|
|
59
100
|
slug: dao.slug,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties, ReactElement, ReactNode } from "react";
|
|
2
|
-
import type { FlowErrorDisplay } from "@stapel/core";
|
|
3
|
-
export declare function ErrorAlert(props: {
|
|
4
|
-
error: FlowErrorDisplay | undefined;
|
|
5
|
-
style?: CSSProperties | undefined;
|
|
6
|
-
testId?: string | undefined;
|
|
7
|
-
action?: ReactNode;
|
|
8
|
-
}): ReactElement | null;
|
|
9
|
-
//# sourceMappingURL=ErrorAlert.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorAlert.d.ts","sourceRoot":"","sources":["../../src/default/ErrorAlert.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,GAAG,YAAY,GAAG,IAAI,CAsBtB"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* The one error surface this pair's default skin renders, so the split copy
|
|
4
|
-
* core produces reaches a screen the same way everywhere (mirrors the
|
|
5
|
-
* docs/notifications/workspaces etalon).
|
|
6
|
-
*
|
|
7
|
-
* Core's `describeFlowError` splits a failure into the sentence a person
|
|
8
|
-
* reads and the technical detail a support agent quotes (owner report
|
|
9
|
-
* 2026-08-09: the status used to be spliced into the sentence, ending in a
|
|
10
|
-
* bare `" (500)"`). The split only helps if a skin renders both halves at
|
|
11
|
-
* their own weights: `message` at normal weight, `detail` muted and small.
|
|
12
|
-
*
|
|
13
|
-
* `undefined` in — including a `detail` core left `undefined` because there
|
|
14
|
-
* was nothing worth quoting — renders nothing rather than an empty line.
|
|
15
|
-
*/
|
|
16
|
-
import { Alert, Typography } from "antd";
|
|
17
|
-
export function ErrorAlert(props) {
|
|
18
|
-
const { error } = props;
|
|
19
|
-
if (!error)
|
|
20
|
-
return null;
|
|
21
|
-
return (_jsx(Alert, { type: "error", showIcon: true, ...(props.style ? { style: props.style } : {}), ...(props.testId ? { "data-testid": props.testId } : {}), ...(props.action ? { action: props.action } : {}), message: error.message, ...(error.detail
|
|
22
|
-
? {
|
|
23
|
-
description: (_jsx(Typography.Text, { type: "secondary", style: { fontSize: 12 }, children: error.detail })),
|
|
24
|
-
}
|
|
25
|
-
: {}) }));
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=ErrorAlert.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorAlert.js","sourceRoot":"","sources":["../../src/default/ErrorAlert.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIzC,MAAM,UAAU,UAAU,CAAC,KAK1B;IACC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACxB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,CACL,KAAC,KAAK,IACJ,IAAI,EAAC,OAAO,EACZ,QAAQ,WACJ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC3C,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACrD,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAClD,OAAO,EAAE,KAAK,CAAC,OAAO,KAClB,CAAC,KAAK,CAAC,MAAM;YACf,CAAC,CAAC;gBACE,WAAW,EAAE,CACX,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YACtD,KAAK,CAAC,MAAM,GACG,CACnB;aACF;YACH,CAAC,CAAC,EAAE,CAAC,GACP,CACH,CAAC;AACJ,CAAC"}
|
package/dist/default/theme.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties, ReactElement, ReactNode } from "react";
|
|
2
|
-
import type { ThemeMode } from "@stapel/tokens-antd";
|
|
3
|
-
export interface ListingsSkinThemeProps {
|
|
4
|
-
/**
|
|
5
|
-
* Light or dark. Defaults to the mode the host's document declares
|
|
6
|
-
* (`resolveThemeMode()`). Pass explicitly to pin a side.
|
|
7
|
-
*/
|
|
8
|
-
readonly mode?: ThemeMode;
|
|
9
|
-
/** Extra styles merged onto the themed root element (layout only — colors
|
|
10
|
-
* come from the mode). */
|
|
11
|
-
readonly style?: CSSProperties;
|
|
12
|
-
readonly children: ReactNode;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* `<ListingsSkinTheme/>` — the self-theming wrapper of
|
|
16
|
-
* `@stapel/listings-react/default`. Standalone use is supported (wrap any
|
|
17
|
-
* composition of the skin's parts once); the shipped surfaces
|
|
18
|
-
* (`ListingCard`, `ListingDetailPane`, `ListingComposerPage`,
|
|
19
|
-
* `MyListingsPane`, `FavoritesPane`)
|
|
20
|
-
* each already wrap themselves, and nested antd
|
|
21
|
-
* `ConfigProvider`s merge, so composing them under one `ListingsSkinTheme`
|
|
22
|
-
* stays correct.
|
|
23
|
-
*/
|
|
24
|
-
export declare function ListingsSkinTheme(props: ListingsSkinThemeProps): ReactElement;
|
|
25
|
-
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/default/theme.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAOpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B;8BAC0B;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,CAmB7E"}
|
package/dist/default/theme.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* The default skin's INTERNAL theme provider — every `/default` surface wraps
|
|
4
|
-
* itself in this, so the skin is readable no matter what the host supplies
|
|
5
|
-
* (tracker #26: a skin set with no internal provider once inherited a theme
|
|
6
|
-
* bridge serving light-mode values inside a dark document — text on
|
|
7
|
-
* background at 1.00:1). The theme derives from `@stapel/tokens` via
|
|
8
|
-
* `@stapel/tokens-antd`'s `toAntdThemeConfig(mode)`; `mode` defaults to what
|
|
9
|
-
* the HOST's document declares (`resolveThemeMode()` — the `data-theme`
|
|
10
|
-
* attribute `tokens.css` keys its dark block on), never to a hardcoded side.
|
|
11
|
-
*
|
|
12
|
-
* This is also what "override styles" means for this pair: a storefront
|
|
13
|
-
* retheming through the §68 token JSON regenerates its `--stapel-*` custom
|
|
14
|
-
* properties and the result page follows with zero code — a far better deal
|
|
15
|
-
* than a prop-per-colour API, and the reason the skin has none.
|
|
16
|
-
*/
|
|
17
|
-
import { useMemo } from "react";
|
|
18
|
-
import { ConfigProvider } from "antd";
|
|
19
|
-
import { resolveThemeMode, toAntdTheme, toAntdThemeConfig, } from "@stapel/tokens-antd";
|
|
20
|
-
/**
|
|
21
|
-
* `<ListingsSkinTheme/>` — the self-theming wrapper of
|
|
22
|
-
* `@stapel/listings-react/default`. Standalone use is supported (wrap any
|
|
23
|
-
* composition of the skin's parts once); the shipped surfaces
|
|
24
|
-
* (`ListingCard`, `ListingDetailPane`, `ListingComposerPage`,
|
|
25
|
-
* `MyListingsPane`, `FavoritesPane`)
|
|
26
|
-
* each already wrap themselves, and nested antd
|
|
27
|
-
* `ConfigProvider`s merge, so composing them under one `ListingsSkinTheme`
|
|
28
|
-
* stays correct.
|
|
29
|
-
*/
|
|
30
|
-
export function ListingsSkinTheme(props) {
|
|
31
|
-
const mode = props.mode ?? resolveThemeMode();
|
|
32
|
-
const theme = useMemo(() => toAntdThemeConfig(mode), [mode]);
|
|
33
|
-
const token = useMemo(() => toAntdTheme(mode), [mode]);
|
|
34
|
-
return (_jsx(ConfigProvider, { theme: theme, children: _jsx("div", { "data-listings-skin-root": true, "data-listings-skin-mode": mode, style: {
|
|
35
|
-
color: token.colorText,
|
|
36
|
-
backgroundColor: token.colorBgContainer,
|
|
37
|
-
...props.style,
|
|
38
|
-
}, children: props.children }) }));
|
|
39
|
-
}
|
|
40
|
-
//# sourceMappingURL=theme.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/default/theme.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAe7B;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA6B;IAC7D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,gBAAgB,EAAE,CAAC;IAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,OAAO,CACL,KAAC,cAAc,IAAC,KAAK,EAAE,KAAK,YAC1B,0EAE2B,IAAI,EAC7B,KAAK,EAAE;gBACL,KAAK,EAAE,KAAK,CAAC,SAAS;gBACtB,eAAe,EAAE,KAAK,CAAC,gBAAgB;gBACvC,GAAG,KAAK,CAAC,KAAK;aACf,YAEA,KAAK,CAAC,QAAQ,GACX,GACS,CAClB,CAAC;AACJ,CAAC"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The one error surface this pair's default skin renders, so the split copy
|
|
3
|
-
* core produces reaches a screen the same way everywhere (mirrors the
|
|
4
|
-
* docs/notifications/workspaces etalon).
|
|
5
|
-
*
|
|
6
|
-
* Core's `describeFlowError` splits a failure into the sentence a person
|
|
7
|
-
* reads and the technical detail a support agent quotes (owner report
|
|
8
|
-
* 2026-08-09: the status used to be spliced into the sentence, ending in a
|
|
9
|
-
* bare `" (500)"`). The split only helps if a skin renders both halves at
|
|
10
|
-
* their own weights: `message` at normal weight, `detail` muted and small.
|
|
11
|
-
*
|
|
12
|
-
* `undefined` in — including a `detail` core left `undefined` because there
|
|
13
|
-
* was nothing worth quoting — renders nothing rather than an empty line.
|
|
14
|
-
*/
|
|
15
|
-
import { Alert, Typography } from "antd";
|
|
16
|
-
import type { CSSProperties, ReactElement, ReactNode } from "react";
|
|
17
|
-
import type { FlowErrorDisplay } from "@stapel/core";
|
|
18
|
-
|
|
19
|
-
export function ErrorAlert(props: {
|
|
20
|
-
error: FlowErrorDisplay | undefined;
|
|
21
|
-
style?: CSSProperties | undefined;
|
|
22
|
-
testId?: string | undefined;
|
|
23
|
-
action?: ReactNode;
|
|
24
|
-
}): ReactElement | null {
|
|
25
|
-
const { error } = props;
|
|
26
|
-
if (!error) return null;
|
|
27
|
-
return (
|
|
28
|
-
<Alert
|
|
29
|
-
type="error"
|
|
30
|
-
showIcon
|
|
31
|
-
{...(props.style ? { style: props.style } : {})}
|
|
32
|
-
{...(props.testId ? { "data-testid": props.testId } : {})}
|
|
33
|
-
{...(props.action ? { action: props.action } : {})}
|
|
34
|
-
message={error.message}
|
|
35
|
-
{...(error.detail
|
|
36
|
-
? {
|
|
37
|
-
description: (
|
|
38
|
-
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
39
|
-
{error.detail}
|
|
40
|
-
</Typography.Text>
|
|
41
|
-
),
|
|
42
|
-
}
|
|
43
|
-
: {})}
|
|
44
|
-
/>
|
|
45
|
-
);
|
|
46
|
-
}
|
package/src/default/theme.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The default skin's INTERNAL theme provider — every `/default` surface wraps
|
|
3
|
-
* itself in this, so the skin is readable no matter what the host supplies
|
|
4
|
-
* (tracker #26: a skin set with no internal provider once inherited a theme
|
|
5
|
-
* bridge serving light-mode values inside a dark document — text on
|
|
6
|
-
* background at 1.00:1). The theme derives from `@stapel/tokens` via
|
|
7
|
-
* `@stapel/tokens-antd`'s `toAntdThemeConfig(mode)`; `mode` defaults to what
|
|
8
|
-
* the HOST's document declares (`resolveThemeMode()` — the `data-theme`
|
|
9
|
-
* attribute `tokens.css` keys its dark block on), never to a hardcoded side.
|
|
10
|
-
*
|
|
11
|
-
* This is also what "override styles" means for this pair: a storefront
|
|
12
|
-
* retheming through the §68 token JSON regenerates its `--stapel-*` custom
|
|
13
|
-
* properties and the result page follows with zero code — a far better deal
|
|
14
|
-
* than a prop-per-colour API, and the reason the skin has none.
|
|
15
|
-
*/
|
|
16
|
-
import { useMemo } from "react";
|
|
17
|
-
import type { CSSProperties, ReactElement, ReactNode } from "react";
|
|
18
|
-
import { ConfigProvider } from "antd";
|
|
19
|
-
import {
|
|
20
|
-
resolveThemeMode,
|
|
21
|
-
toAntdTheme,
|
|
22
|
-
toAntdThemeConfig,
|
|
23
|
-
} from "@stapel/tokens-antd";
|
|
24
|
-
import type { ThemeMode } from "@stapel/tokens-antd";
|
|
25
|
-
|
|
26
|
-
export interface ListingsSkinThemeProps {
|
|
27
|
-
/**
|
|
28
|
-
* Light or dark. Defaults to the mode the host's document declares
|
|
29
|
-
* (`resolveThemeMode()`). Pass explicitly to pin a side.
|
|
30
|
-
*/
|
|
31
|
-
readonly mode?: ThemeMode;
|
|
32
|
-
/** Extra styles merged onto the themed root element (layout only — colors
|
|
33
|
-
* come from the mode). */
|
|
34
|
-
readonly style?: CSSProperties;
|
|
35
|
-
readonly children: ReactNode;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* `<ListingsSkinTheme/>` — the self-theming wrapper of
|
|
40
|
-
* `@stapel/listings-react/default`. Standalone use is supported (wrap any
|
|
41
|
-
* composition of the skin's parts once); the shipped surfaces
|
|
42
|
-
* (`ListingCard`, `ListingDetailPane`, `ListingComposerPage`,
|
|
43
|
-
* `MyListingsPane`, `FavoritesPane`)
|
|
44
|
-
* each already wrap themselves, and nested antd
|
|
45
|
-
* `ConfigProvider`s merge, so composing them under one `ListingsSkinTheme`
|
|
46
|
-
* stays correct.
|
|
47
|
-
*/
|
|
48
|
-
export function ListingsSkinTheme(props: ListingsSkinThemeProps): ReactElement {
|
|
49
|
-
const mode = props.mode ?? resolveThemeMode();
|
|
50
|
-
const theme = useMemo(() => toAntdThemeConfig(mode), [mode]);
|
|
51
|
-
const token = useMemo(() => toAntdTheme(mode), [mode]);
|
|
52
|
-
return (
|
|
53
|
-
<ConfigProvider theme={theme}>
|
|
54
|
-
<div
|
|
55
|
-
data-listings-skin-root
|
|
56
|
-
data-listings-skin-mode={mode}
|
|
57
|
-
style={{
|
|
58
|
-
color: token.colorText,
|
|
59
|
-
backgroundColor: token.colorBgContainer,
|
|
60
|
-
...props.style,
|
|
61
|
-
}}
|
|
62
|
-
>
|
|
63
|
-
{props.children}
|
|
64
|
-
</div>
|
|
65
|
-
</ConfigProvider>
|
|
66
|
-
);
|
|
67
|
-
}
|