@stapel/listings-react 0.27.0 → 0.28.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 +78 -0
- package/README.md +56 -7
- package/dist/default/ListingActions.d.ts +8 -1
- package/dist/default/ListingActions.d.ts.map +1 -1
- package/dist/default/ListingActions.js +1 -1
- package/dist/default/ListingActions.js.map +1 -1
- package/dist/default/ListingDetailPane.d.ts +52 -1
- package/dist/default/ListingDetailPane.d.ts.map +1 -1
- package/dist/default/ListingDetailPane.js +4 -2
- package/dist/default/ListingDetailPane.js.map +1 -1
- package/dist/default/ShareAction.d.ts +16 -1
- package/dist/default/ShareAction.d.ts.map +1 -1
- package/dist/default/ShareAction.js +16 -9
- package/dist/default/ShareAction.js.map +1 -1
- package/dist/default/actionRow.d.ts +24 -0
- package/dist/default/actionRow.d.ts.map +1 -1
- package/dist/default/actionRow.js +50 -7
- package/dist/default/actionRow.js.map +1 -1
- package/dist/default/index.d.ts +1 -1
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +1 -1
- package/dist/default/index.js.map +1 -1
- package/dist/headless/Share.d.ts +60 -2
- package/dist/headless/Share.d.ts.map +1 -1
- package/dist/headless/Share.js +55 -3
- package/dist/headless/Share.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/model/cardBadges.d.ts +25 -0
- package/dist/model/cardBadges.d.ts.map +1 -1
- package/dist/model/cardBadges.js +41 -5
- package/dist/model/cardBadges.js.map +1 -1
- package/dist/model/draft.d.ts +42 -1
- package/dist/model/draft.d.ts.map +1 -1
- package/dist/model/draft.js +40 -3
- package/dist/model/draft.js.map +1 -1
- package/llms.txt +1 -1
- package/manifest.json +8 -1
- package/nav-manifest.json +1 -1
- package/package.json +1 -1
- package/src/analytics/generated/events.json +1 -1
- package/src/default/ListingActions.tsx +9 -1
- package/src/default/ListingDetailPane.tsx +57 -3
- package/src/default/ShareAction.tsx +36 -10
- package/src/default/actionRow.ts +55 -7
- package/src/default/index.ts +1 -0
- package/src/headless/Share.tsx +98 -5
- package/src/index.ts +7 -0
- package/src/model/cardBadges.ts +42 -5
- package/src/model/draft.ts +61 -3
package/dist/model/draft.d.ts
CHANGED
|
@@ -98,6 +98,26 @@ export declare function draftValuesFromWire(draft: ListingDraft, options?: Empty
|
|
|
98
98
|
* is the one place the published values live.
|
|
99
99
|
*/
|
|
100
100
|
export declare function draftValuesFromDetail(detail: ListingDetail, featuresDto: FeaturesDto, options?: EmptyDraftOptions): ListingDraftValues;
|
|
101
|
+
/** One field of the `save-draft` body, by the name the wire uses. */
|
|
102
|
+
export type ListingDraftField = keyof ListingDraftPatch;
|
|
103
|
+
/** Options for {@link draftPatchFromValues}. */
|
|
104
|
+
export interface DraftPatchOptions {
|
|
105
|
+
/**
|
|
106
|
+
* NAME THE FIELDS THIS SAVE IS WRITING, and the body carries no others.
|
|
107
|
+
*
|
|
108
|
+
* `save-draft` REPLACES every field in the body — it does not merge — so a
|
|
109
|
+
* body is not "the values I have", it is "the fields I am claiming". A save
|
|
110
|
+
* fired by one control (a photo settling, a blurred title) previously
|
|
111
|
+
* claimed all fourteen, which is only harmless while every one of them is
|
|
112
|
+
* loaded and true.
|
|
113
|
+
*
|
|
114
|
+
* Given, only these keys are spelled; the omission rules below still apply
|
|
115
|
+
* inside the selection, so naming `features_draft` without a schema still
|
|
116
|
+
* writes nothing. Absent, the whole body is sent, which is right for the
|
|
117
|
+
* composer's own save — it holds every value on the form.
|
|
118
|
+
*/
|
|
119
|
+
readonly fields?: readonly ListingDraftField[];
|
|
120
|
+
}
|
|
101
121
|
/**
|
|
102
122
|
* The composer's values → the `save-draft` body.
|
|
103
123
|
*
|
|
@@ -108,8 +128,29 @@ export declare function draftValuesFromDetail(detail: ListingDetail, featuresDto
|
|
|
108
128
|
* (`error.400.listing_feature_not_allowed`), which is why
|
|
109
129
|
* {@link retainKnownFeatureValues} prunes on the way in rather than letting
|
|
110
130
|
* the server explain it.
|
|
131
|
+
*
|
|
132
|
+
* ── NO SCHEMA IS NOT AN EMPTY ANSWER SHEET ───────────────────────────────
|
|
133
|
+
*
|
|
134
|
+
* An absent or empty `features` used to produce `features_draft: {}`, and
|
|
135
|
+
* `save-draft` REPLACES that map rather than merging into it — so a save that
|
|
136
|
+
* left before the category's schema arrived DELETED every characteristic the
|
|
137
|
+
* row was holding, while the form on screen still showed them. Measured by a
|
|
138
|
+
* live container over two cold loads of one draft: a reopen's own
|
|
139
|
+
* settled-photo save fires in the first commit, before the row's category has
|
|
140
|
+
* even been adopted, and the row alternated between the draft's answers and
|
|
141
|
+
* none of them (a client storefront's README, "Named gaps").
|
|
142
|
+
*
|
|
143
|
+
* `{}` is a claim — "this listing has no characteristics" — and a caller with
|
|
144
|
+
* no schema is in no position to make it. So the key is OMITTED entirely
|
|
145
|
+
* whenever there is no schema to tag values with, and `save-draft` then
|
|
146
|
+
* leaves the stored map exactly as it was. A category that genuinely declares
|
|
147
|
+
* no features writes nothing either, which is the same answer arrived at
|
|
148
|
+
* honestly: there is nothing to say and the row already says it.
|
|
149
|
+
*
|
|
150
|
+
* {@link DraftPatchOptions.fields} is the general form of the same rule — a
|
|
151
|
+
* save that names what it is writing cannot erase what it is not.
|
|
111
152
|
*/
|
|
112
|
-
export declare function draftPatchFromValues(values: ListingDraftValues, features: readonly FeatureDef[]): ListingDraftPatch;
|
|
153
|
+
export declare function draftPatchFromValues(values: ListingDraftValues, features: readonly FeatureDef[] | undefined, options?: DraftPatchOptions): ListingDraftPatch;
|
|
113
154
|
/**
|
|
114
155
|
* The body for CREATING a draft: the category if there is one, `{}` if there
|
|
115
156
|
* is not.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAGzB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;mEAC+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,cAAc,EAAE,eAM5B,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;iFAE6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;iDACiD;AACjD,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CAepB;AAMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CAwBpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CA4BpB;AA4BD
|
|
1
|
+
{"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAGzB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;mEAC+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,cAAc,EAAE,eAM5B,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;iFAE6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;iDACiD;AACjD,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CAepB;AAMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CAwBpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,iBAAsB,GAC9B,kBAAkB,CA4BpB;AA4BD,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,MAAM,iBAAiB,CAAC;AAExD,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,EAC3C,OAAO,GAAE,iBAAsB,GAC9B,iBAAiB,CA0CnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAItE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,SAAS,UAAU,EAAE,GAC9B,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOnC;AAED;;;eAGe;AACf,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,SAAS,UAAU,EAAE,GAC9B,SAAS,MAAM,EAAE,CAKnB"}
|
package/dist/model/draft.js
CHANGED
|
@@ -126,9 +126,30 @@ function toWireFeatures(features, values) {
|
|
|
126
126
|
* (`error.400.listing_feature_not_allowed`), which is why
|
|
127
127
|
* {@link retainKnownFeatureValues} prunes on the way in rather than letting
|
|
128
128
|
* the server explain it.
|
|
129
|
+
*
|
|
130
|
+
* ── NO SCHEMA IS NOT AN EMPTY ANSWER SHEET ───────────────────────────────
|
|
131
|
+
*
|
|
132
|
+
* An absent or empty `features` used to produce `features_draft: {}`, and
|
|
133
|
+
* `save-draft` REPLACES that map rather than merging into it — so a save that
|
|
134
|
+
* left before the category's schema arrived DELETED every characteristic the
|
|
135
|
+
* row was holding, while the form on screen still showed them. Measured by a
|
|
136
|
+
* live container over two cold loads of one draft: a reopen's own
|
|
137
|
+
* settled-photo save fires in the first commit, before the row's category has
|
|
138
|
+
* even been adopted, and the row alternated between the draft's answers and
|
|
139
|
+
* none of them (a client storefront's README, "Named gaps").
|
|
140
|
+
*
|
|
141
|
+
* `{}` is a claim — "this listing has no characteristics" — and a caller with
|
|
142
|
+
* no schema is in no position to make it. So the key is OMITTED entirely
|
|
143
|
+
* whenever there is no schema to tag values with, and `save-draft` then
|
|
144
|
+
* leaves the stored map exactly as it was. A category that genuinely declares
|
|
145
|
+
* no features writes nothing either, which is the same answer arrived at
|
|
146
|
+
* honestly: there is nothing to say and the row already says it.
|
|
147
|
+
*
|
|
148
|
+
* {@link DraftPatchOptions.fields} is the general form of the same rule — a
|
|
149
|
+
* save that names what it is writing cannot erase what it is not.
|
|
129
150
|
*/
|
|
130
|
-
export function draftPatchFromValues(values, features) {
|
|
131
|
-
|
|
151
|
+
export function draftPatchFromValues(values, features, options = {}) {
|
|
152
|
+
const full = {
|
|
132
153
|
// Omitted while unchosen rather than sent as `""`: a draft is allowed to
|
|
133
154
|
// have no category (0.21.4), and `""` is not "no category" on the wire —
|
|
134
155
|
// it is an empty id the serializer refuses. The category is written by
|
|
@@ -147,7 +168,11 @@ export function draftPatchFromValues(values, features) {
|
|
|
147
168
|
// sent here is discarded. Sending one would be a claim the wire ignores.
|
|
148
169
|
lat_draft: values.location.lat,
|
|
149
170
|
lon_draft: values.location.lon,
|
|
150
|
-
|
|
171
|
+
// Omitted, not emptied, when there is no schema to tag with — see the
|
|
172
|
+
// doc above. `{}` is a claim about the listing; silence is not.
|
|
173
|
+
...(features !== undefined && features.length > 0
|
|
174
|
+
? { features_draft: toWireFeatures(features, values.features) }
|
|
175
|
+
: {}),
|
|
151
176
|
countable: values.countable,
|
|
152
177
|
// The pair mirrors the model's cross-field rule rather than sending a
|
|
153
178
|
// contradiction: a service carries no quantity, and `validate_countable
|
|
@@ -155,6 +180,18 @@ export function draftPatchFromValues(values, features) {
|
|
|
155
180
|
stock_quantity: values.countable ? values.stockQuantity : null,
|
|
156
181
|
auto_republish: values.autoRepublish,
|
|
157
182
|
};
|
|
183
|
+
const named = options.fields;
|
|
184
|
+
if (named === undefined)
|
|
185
|
+
return full;
|
|
186
|
+
// A selection, not a second body: the field's VALUE is still whatever the
|
|
187
|
+
// rules above produced, so a named field the rules omit stays omitted.
|
|
188
|
+
const wanted = new Set(named);
|
|
189
|
+
const out = {};
|
|
190
|
+
for (const [key, value] of Object.entries(full)) {
|
|
191
|
+
if (wanted.has(key))
|
|
192
|
+
out[key] = value;
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
158
195
|
}
|
|
159
196
|
/**
|
|
160
197
|
* The body for CREATING a draft: the category if there is one, `{}` if there
|
package/dist/model/draft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAM1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAgC3D,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,UAAU,EAAE,EAAE;IACd,aAAa,EAAE,EAAE;IACjB,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,OAAO,EAAE,EAAE;CACZ,CAAC;AAqCF;iDACiD;AACjD,MAAM,UAAU,gBAAgB,CAC9B,UAA6B,EAAE;IAE/B,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE;QACpC,KAAK,EAAE,EAAE;QACT,WAAW,EAAE,EAAE;QACf,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,wBAAwB;QACtD,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;QAChC,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE,cAAc;QACxB,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,KAAK;QAChB,aAAa,EAAE,IAAI;QACnB,aAAa,EAAE,KAAK;KACrB,CAAC;AACJ,CAAC;AAED,SAAS,IAAI,CAAC,KAAgC;IAC5C,OAAO,KAAK,IAAI,EAAE,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAmB,EACnB,UAA6B,EAAE;IAE/B,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;QACnC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;QAC9B,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;QAC1C,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;YACjE,CAAC,CAAC,KAAK,CAAC,QAAQ;YAChB,CAAC,CAAC,IAAI,CAAC,QAAQ;QACjB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC9B,MAAM,EAAE,KAAK,CAAC,YAAY,IAAI,EAAE;QAChC,QAAQ,EAAE;YACR,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;YACzC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC;YAC/C,GAAG,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;YAC5B,GAAG,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;YAC5B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;SACnC;QACD,QAAQ,EAAE,eAAe,CAAC,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAgB,CAAC;QACtE,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,KAAK;QACnC,aAAa,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI;QAC3C,aAAa,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;KAC7C,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAqB,EACrB,WAAwB,EACxB,UAA6B,EAAE;IAE/B,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO;QACL,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,0DAA0D;QAC1D,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACpC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QACzB,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACrC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;YACnE,CAAC,CAAC,MAAM,CAAC,QAAQ;YACjB,CAAC,CAAC,IAAI,CAAC,QAAQ;QACjB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;QAC3B,QAAQ,EAAE;YACR,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YACpC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;YAC1C,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,IAAI;YACvB,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,IAAI;YACvB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;SAC9B;QACD,QAAQ,EAAE,eAAe,CAAC,WAAW,CAAC;QACtC,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,KAAK;QACpC,aAAa,EAAE,MAAM,CAAC,cAAc,IAAI,IAAI;QAC5C,aAAa,EAAE,MAAM,CAAC,cAAc,IAAI,KAAK;KAC9C,CAAC;AACJ,CAAC;AAMD;;;;;;;;;;;;;;GAcG;AACH,SAAS,cAAc,CACrB,QAA+B,EAC/B,MAAyC;IAEzC,OAAO,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAiC,CAAC;AACzE,CAAC;
|
|
1
|
+
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/model/draft.ts"],"names":[],"mappings":"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"}
|
package/llms.txt
CHANGED
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$generated": "by scripts/gen-manifest.mjs — do not edit; drift-gated (pnpm gen:manifest:check)",
|
|
3
3
|
"package": "@stapel/listings-react",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.28.0",
|
|
5
5
|
"backend": {
|
|
6
6
|
"module": "stapel-listings",
|
|
7
7
|
"contract": ">=0.22 <0.23"
|
|
@@ -1266,6 +1266,7 @@
|
|
|
1266
1266
|
"NO_ADDRESS",
|
|
1267
1267
|
"OWNER_TRANSITIONS",
|
|
1268
1268
|
"PRICE_FIELD",
|
|
1269
|
+
"SHARE_COARSE_MEDIA",
|
|
1269
1270
|
"SHARE_COPIED_MS",
|
|
1270
1271
|
"SHARE_NETWORKS",
|
|
1271
1272
|
"Share",
|
|
@@ -1276,6 +1277,7 @@
|
|
|
1276
1277
|
"browserAddress",
|
|
1277
1278
|
"canDelete",
|
|
1278
1279
|
"canTransition",
|
|
1280
|
+
"captionName",
|
|
1279
1281
|
"cardBadgeText",
|
|
1280
1282
|
"cardBadgeTexts",
|
|
1281
1283
|
"countedTabOf",
|
|
@@ -1301,6 +1303,7 @@
|
|
|
1301
1303
|
"flowEndpoints",
|
|
1302
1304
|
"formatSpecValue",
|
|
1303
1305
|
"hasCardBadgeContract",
|
|
1306
|
+
"hasCoarsePointer",
|
|
1304
1307
|
"hasNativeShare",
|
|
1305
1308
|
"isBatchValid",
|
|
1306
1309
|
"isListingViewed",
|
|
@@ -1321,6 +1324,7 @@
|
|
|
1321
1324
|
"navEntries",
|
|
1322
1325
|
"ownerMoves",
|
|
1323
1326
|
"pageKey",
|
|
1327
|
+
"preferNativeShare",
|
|
1324
1328
|
"publishRefusal",
|
|
1325
1329
|
"registerListingsI18n",
|
|
1326
1330
|
"resolveShareUrl",
|
|
@@ -1373,6 +1377,7 @@
|
|
|
1373
1377
|
"ComposeStage",
|
|
1374
1378
|
"CreateListingsRuntimeOptions",
|
|
1375
1379
|
"DeleteResponse",
|
|
1380
|
+
"DraftPatchOptions",
|
|
1376
1381
|
"EmptyDraftOptions",
|
|
1377
1382
|
"FavoriteInput",
|
|
1378
1383
|
"FavoriteToggleBag",
|
|
@@ -1391,6 +1396,7 @@
|
|
|
1391
1396
|
"ListingDetailBag",
|
|
1392
1397
|
"ListingDetailData",
|
|
1393
1398
|
"ListingDraft",
|
|
1399
|
+
"ListingDraftField",
|
|
1394
1400
|
"ListingDraftLimits",
|
|
1395
1401
|
"ListingDraftPatch",
|
|
1396
1402
|
"ListingDraftValues",
|
|
@@ -1444,6 +1450,7 @@
|
|
|
1444
1450
|
"ShareChannel",
|
|
1445
1451
|
"ShareLink",
|
|
1446
1452
|
"ShareNetwork",
|
|
1453
|
+
"SharePreference",
|
|
1447
1454
|
"ShareTarget",
|
|
1448
1455
|
"UseFavoritesOptions",
|
|
1449
1456
|
"UseListingActionsOptions",
|
package/nav-manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stapel/listings-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Headless React pair for stapel-listings: a typed client over the draft twin, the composer that turns a category schema into a submittable listing, and the owner's dashboard. Two independent axes are rendered as two — lifecycle `status` decides visibility and `moderation_status` decides nothing about it, so an edit to a live listing stays live and says 'under review' instead of vanishing. Feature values are drawn and mirrored by @stapel/attributes-react; photos arrive as an injected upload bag whose `refs` ARE `images_draft` and whose `settled` gates the submit; a publish refusal is routed onto the control that caused it by slug. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@ import { Flex, Typography } from "antd";
|
|
|
46
46
|
import type { SignInCta } from "@stapel/core";
|
|
47
47
|
import { useT } from "@stapel/core";
|
|
48
48
|
import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
|
|
49
|
-
import type { ShareChannel } from "../headless/Share.js";
|
|
49
|
+
import type { ShareChannel, SharePreference } from "../headless/Share.js";
|
|
50
50
|
import { FavoriteHeart } from "./favorite.js";
|
|
51
51
|
import { ShareAction } from "./ShareAction.js";
|
|
52
52
|
import {
|
|
@@ -92,6 +92,13 @@ export interface ListingActionsProps {
|
|
|
92
92
|
readonly shareTitle?: string;
|
|
93
93
|
/** A line under it in the sheet — the price, typically. */
|
|
94
94
|
readonly shareText?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Which arm the share control uses — handed straight to
|
|
97
|
+
* `<ShareAction prefer>`, which is where the reading and its measurement
|
|
98
|
+
* are written down. Default `"auto"`: the platform sheet on a thumb, this
|
|
99
|
+
* pair's menu on a mouse.
|
|
100
|
+
*/
|
|
101
|
+
readonly sharePrefer?: SharePreference;
|
|
95
102
|
readonly onShared?: (channel: ShareChannel) => void;
|
|
96
103
|
/** The container's sign-in door for the heart. */
|
|
97
104
|
readonly signIn?: SignInCta;
|
|
@@ -187,6 +194,7 @@ export function ListingActions(props: ListingActionsProps): ReactElement | null
|
|
|
187
194
|
{...(props.shareUrl !== undefined ? { url: props.shareUrl } : {})}
|
|
188
195
|
{...(props.shareTitle !== undefined ? { title: props.shareTitle } : {})}
|
|
189
196
|
{...(props.shareText !== undefined ? { text: props.shareText } : {})}
|
|
197
|
+
{...(props.sharePrefer !== undefined ? { prefer: props.sharePrefer } : {})}
|
|
190
198
|
{...(props.onShared !== undefined ? { onShared: props.onShared } : {})}
|
|
191
199
|
/>
|
|
192
200
|
) : null}
|
|
@@ -102,7 +102,7 @@ import { useListingActions } from "../headless/ListingActions.js";
|
|
|
102
102
|
import { asFeatureDaoList, featureValuesForDisplay } from "../model/features.js";
|
|
103
103
|
import { formatSpecValue } from "../model/featureText.js";
|
|
104
104
|
import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
|
|
105
|
-
import type { ShareChannel } from "../headless/Share.js";
|
|
105
|
+
import type { ShareChannel, SharePreference } from "../headless/Share.js";
|
|
106
106
|
import { GateReasonPopover } from "./GateReasonPopover.js";
|
|
107
107
|
import { ListingActions } from "./ListingActions.js";
|
|
108
108
|
import type { ListingActionsConfig } from "./ListingActions.js";
|
|
@@ -271,6 +271,14 @@ export interface ListingDetailPaneProps
|
|
|
271
271
|
* somebody sends to a friend. See `useShare`.
|
|
272
272
|
*/
|
|
273
273
|
readonly shareUrl?: string;
|
|
274
|
+
/**
|
|
275
|
+
* Which arm the share control uses — handed to `<ShareAction prefer>`
|
|
276
|
+
* through the cluster. Default `"auto"`: the platform sheet where the
|
|
277
|
+
* primary pointer is coarse, this pair's menu on a mouse. See
|
|
278
|
+
* `SharePreference` for the measurement that made the pointer part of the
|
|
279
|
+
* question (§25).
|
|
280
|
+
*/
|
|
281
|
+
readonly sharePrefer?: SharePreference;
|
|
274
282
|
/** Analytics: which channel a completed share went through. */
|
|
275
283
|
readonly onShared?: (channel: ShareChannel) => void;
|
|
276
284
|
/**
|
|
@@ -412,6 +420,49 @@ export interface ListingDetailPaneProps
|
|
|
412
420
|
* Default `3`, byte-compatible for every existing mount.
|
|
413
421
|
*/
|
|
414
422
|
readonly headingLevel?: 1 | 2 | 3;
|
|
423
|
+
/**
|
|
424
|
+
* WHERE THE BUY COLUMN'S STICKY TOP EDGE IS — the offset of whatever chrome
|
|
425
|
+
* is pinned above this page (`layout="split"` only; the one-column arm has
|
|
426
|
+
* no sticky column).
|
|
427
|
+
*
|
|
428
|
+
* ```tsx
|
|
429
|
+
* // the height <PublicShell> publishes, read rather than restated
|
|
430
|
+
* <ListingDetailPane layout="split" buyTop="var(--stapel-header-height)" />
|
|
431
|
+
* ```
|
|
432
|
+
*
|
|
433
|
+
* The column is `position: sticky; top: 16px` written INLINE, and an inline
|
|
434
|
+
* declaration is beaten by nothing short of `!important` — so a host with a
|
|
435
|
+
* pinned header had no way to say "start below it". Measured on the stand
|
|
436
|
+
* (D456): the storefront's header is sticky and 64px tall, and at any scroll
|
|
437
|
+
* depth the top of the buy column — the price's own first twenty pixels —
|
|
438
|
+
* sat UNDER it. Same seam and same argument as `<SearchPage railTop>`, which
|
|
439
|
+
* this prop is deliberately spelled after.
|
|
440
|
+
*
|
|
441
|
+
* A number is pixels; a string is taken as written (a `var()`, a `calc()`,
|
|
442
|
+
* `"4rem"`). Default `spacing[4]` — 16px, exactly where the column has
|
|
443
|
+
* always started — so an existing mount is byte-compatible.
|
|
444
|
+
*/
|
|
445
|
+
readonly buyTop?: number | string;
|
|
446
|
+
/**
|
|
447
|
+
* HOW WIDE THE PANE MAY GET — the `max-width` it writes on its own root.
|
|
448
|
+
*
|
|
449
|
+
* Default is the constant for the arm on screen: {@link DETAIL_MEASURE}
|
|
450
|
+
* (60rem) in `"column"`, {@link DETAIL_SPLIT_MEASURE} (75rem) in `"split"`,
|
|
451
|
+
* so no existing mount changes shape. Anything CSS `max-width` takes is
|
|
452
|
+
* accepted (`"80rem"`, `1280`, `"100%"`).
|
|
453
|
+
*
|
|
454
|
+
* `"none"` removes the cap, and it is the answer for a pane mounted inside
|
|
455
|
+
* a page frame that already decided the measure. That case is not
|
|
456
|
+
* hypothetical: measured on the stand at 1440 (D457), the listing page's
|
|
457
|
+
* content ended at x=1224 with 216px of empty gutter beside it while every
|
|
458
|
+
* other page of the same site ran to the frame's edge — a second, lower cap
|
|
459
|
+
* inside a container that already had one. The cap is written INLINE, so
|
|
460
|
+
* the container could not outrank it without `!important` and reached for
|
|
461
|
+
* `min-inline-size: 100%` instead (a minimum beats a maximum by the sizing
|
|
462
|
+
* rules); this prop is that workaround's replacement, and the same seam
|
|
463
|
+
* `<CategoryPage measure>` already offers.
|
|
464
|
+
*/
|
|
465
|
+
readonly measure?: number | string;
|
|
415
466
|
readonly footer?: ReactNode;
|
|
416
467
|
}
|
|
417
468
|
|
|
@@ -592,7 +643,7 @@ export function ListingDetailPane(props: ListingDetailPaneProps): ReactElement {
|
|
|
592
643
|
<SkinTheme
|
|
593
644
|
surface="base"
|
|
594
645
|
style={{
|
|
595
|
-
maxWidth: split ? DETAIL_SPLIT_MEASURE : DETAIL_MEASURE,
|
|
646
|
+
maxWidth: props.measure ?? (split ? DETAIL_SPLIT_MEASURE : DETAIL_MEASURE),
|
|
596
647
|
// See `gutter`: a frame that already placed the page edge does not get
|
|
597
648
|
// a second one stacked inside it.
|
|
598
649
|
padding: props.gutter === "shell" ? 0 : spacing[4],
|
|
@@ -770,6 +821,9 @@ export function ListingDetailPane(props: ListingDetailPaneProps): ReactElement {
|
|
|
770
821
|
{...(listing.title !== undefined && listing.title !== null
|
|
771
822
|
? { shareTitle: listing.title }
|
|
772
823
|
: {})}
|
|
824
|
+
{...(props.sharePrefer !== undefined
|
|
825
|
+
? { sharePrefer: props.sharePrefer }
|
|
826
|
+
: {})}
|
|
773
827
|
{...(props.onShared !== undefined ? { onShared: props.onShared } : {})}
|
|
774
828
|
/>
|
|
775
829
|
);
|
|
@@ -1171,7 +1225,7 @@ export function ListingDetailPane(props: ListingDetailPaneProps): ReactElement {
|
|
|
1171
1225
|
data-testid="listings-detail-buy-column"
|
|
1172
1226
|
style={{
|
|
1173
1227
|
position: "sticky",
|
|
1174
|
-
top: spacing[4],
|
|
1228
|
+
top: props.buyTop ?? spacing[4],
|
|
1175
1229
|
alignSelf: "start",
|
|
1176
1230
|
}}
|
|
1177
1231
|
>
|
|
@@ -10,16 +10,22 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ── One control, two renderings, and the DEVICE picks ─────────────────────
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* On a device driven with a THUMB the press opens the PLATFORM's own sheet:
|
|
14
|
+
* the person's own apps, in their own order, including the ones we have never
|
|
15
|
+
* heard of. A library that drew its own list of four networks on a phone would
|
|
16
|
+
* be offering a worse version of something the operating system already does
|
|
17
|
+
* better.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* is
|
|
19
|
+
* Everywhere else the press opens a small menu: copy the link, and the three
|
|
20
|
+
* networks a Russian-speaking marketplace actually receives traffic from. This
|
|
21
|
+
* is the DESKTOP rendering, not a fallback for old browsers, which is why it
|
|
22
|
+
* is built rather than apologised for.
|
|
23
|
+
*
|
|
24
|
+
* The reading that decides is the primary POINTER and not `navigator.share`
|
|
25
|
+
* alone: desktop Chrome on macOS has the API, so the capability question sent
|
|
26
|
+
* every desktop share to the OS sheet and left this menu unreachable on the
|
|
27
|
+
* platform it was written for (§25). {@link ShareActionProps.prefer} is where
|
|
28
|
+
* a host overrules the reading in either direction.
|
|
23
29
|
*
|
|
24
30
|
* ── The menu is a Popover, and that is an exception with an argument ──────
|
|
25
31
|
*
|
|
@@ -60,7 +66,11 @@ import { SkinButton as Button, ErrorAlert } from "@stapel/tokens-antd/skin";
|
|
|
60
66
|
import { useT } from "@stapel/core";
|
|
61
67
|
import { spacing } from "@stapel/tokens";
|
|
62
68
|
import { useShare } from "../headless/Share.js";
|
|
63
|
-
import type {
|
|
69
|
+
import type {
|
|
70
|
+
ShareChannel,
|
|
71
|
+
ShareNetwork,
|
|
72
|
+
SharePreference,
|
|
73
|
+
} from "../headless/Share.js";
|
|
64
74
|
import { LISTINGS_I18N_KEYS } from "../i18n/keys.js";
|
|
65
75
|
import {
|
|
66
76
|
LISTING_ACTIONS_STYLE_HREF,
|
|
@@ -104,6 +114,21 @@ export interface ShareActionProps {
|
|
|
104
114
|
* for it.
|
|
105
115
|
*/
|
|
106
116
|
readonly shape?: "default" | "circle";
|
|
117
|
+
/**
|
|
118
|
+
* WHICH ARM THIS SURFACE WANTS — see {@link SharePreference}.
|
|
119
|
+
*
|
|
120
|
+
* Default `"auto"`: the platform sheet where the primary pointer is coarse
|
|
121
|
+
* AND `navigator.share` exists, this pair's menu everywhere else. The
|
|
122
|
+
* default changed in this release, and it changed because of a measurement
|
|
123
|
+
* (§25): desktop Chrome on macOS reports `navigator.share`, so a pair that
|
|
124
|
+
* asked only the capability opened the OS sheet on the desktop and the
|
|
125
|
+
* copy-link menu — three networks and a clipboard row, built for exactly
|
|
126
|
+
* that platform — could not be reached there at all.
|
|
127
|
+
*
|
|
128
|
+
* `"native"` restores the capability-only behaviour by name; `"menu"` pins
|
|
129
|
+
* this pair's menu on every device.
|
|
130
|
+
*/
|
|
131
|
+
readonly prefer?: SharePreference;
|
|
107
132
|
readonly style?: CSSProperties;
|
|
108
133
|
}
|
|
109
134
|
|
|
@@ -115,6 +140,7 @@ export function ShareAction(props: ShareActionProps): ReactElement {
|
|
|
115
140
|
url: props.url,
|
|
116
141
|
title: props.title,
|
|
117
142
|
text: props.text,
|
|
143
|
+
...(props.prefer !== undefined ? { prefer: props.prefer } : {}),
|
|
118
144
|
...(props.onShared !== undefined ? { onShared: props.onShared } : {}),
|
|
119
145
|
});
|
|
120
146
|
const label = t(LISTINGS_I18N_KEYS.shareAction);
|
package/src/default/actionRow.ts
CHANGED
|
@@ -86,13 +86,62 @@ export const LISTING_ACTION_HIT: number = controls["height-phone"];
|
|
|
86
86
|
*/
|
|
87
87
|
export const LISTING_CARD_ACTION_HIT = 36;
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* HOW MANY TIMES THE HIT-TARGET CLASS IS REPEATED IN ITS OWN SELECTOR — and
|
|
91
|
+
* why a repeat rather than a number typed once (D450).
|
|
92
|
+
*
|
|
93
|
+
* Measured on the live listing page: the heart and the share glyph were
|
|
94
|
+
* **32 × 44**, not 44 × 44. The block axis survived and the inline one did
|
|
95
|
+
* not, because antd's circle shape ships
|
|
96
|
+
*
|
|
97
|
+
* `:where(…).ant-btn.ant-btn-circle.ant-btn{min-width:var(--ant-control-height)}`
|
|
98
|
+
*
|
|
99
|
+
* — three classes, specificity (0,3,0), against this sheet's single class
|
|
100
|
+
* (0,1,0). `:where()` adds nothing, and a media query adds nothing either, so
|
|
101
|
+
* the only thing that decides is the class count: antd's 32 won and there was
|
|
102
|
+
* no viewport at which it did not.
|
|
103
|
+
*
|
|
104
|
+
* A repeated class is the one way to outrank it without `!important`. Four
|
|
105
|
+
* repeats — (0,4,0) — clear antd's three with one to spare, and the selector
|
|
106
|
+
* still matches exactly the same element, so nothing about WHAT the rule
|
|
107
|
+
* applies to changes. `!important` was refused deliberately: a host that
|
|
108
|
+
* genuinely wants a different target must be able to say so with a selector,
|
|
109
|
+
* and this sheet's whole argument (see `movableCluster.tsx`) is that a pair's
|
|
110
|
+
* geometry should never force one on somebody else.
|
|
111
|
+
*/
|
|
112
|
+
export const LISTING_ACTION_SPECIFICITY = 4;
|
|
113
|
+
|
|
114
|
+
/** A class name repeated {@link LISTING_ACTION_SPECIFICITY} times — the
|
|
115
|
+
* selector that beats antd's circle. */
|
|
116
|
+
function outranking(className: string): string {
|
|
117
|
+
return `.${className}`.repeat(LISTING_ACTION_SPECIFICITY);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The floor, in BOTH spellings of the same axis.
|
|
122
|
+
*
|
|
123
|
+
* A browser cascades `min-inline-size` and `min-width` together and keeps the
|
|
124
|
+
* winner, so in a browser the logical pair alone would be enough once the
|
|
125
|
+
* selector outranks antd's. The physical pair is written beside it because
|
|
126
|
+
* the declaration being beaten is spelled physically, and an engine that does
|
|
127
|
+
* NOT merge the two names (jsdom, where this rule is asserted) would leave
|
|
128
|
+
* antd's `min-width:32px` standing beside our `min-inline-size:44px` and call
|
|
129
|
+
* that a pass. Two spellings of one number, and the number has one source.
|
|
130
|
+
*/
|
|
131
|
+
function floor(size: number): string {
|
|
132
|
+
const px = `${String(size)}px`;
|
|
133
|
+
return (
|
|
134
|
+
`min-inline-size:${px};min-block-size:${px};` +
|
|
135
|
+
`min-width:${px};min-height:${px}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
89
139
|
export function actionRowCss(): string {
|
|
90
|
-
const action =
|
|
91
|
-
const cardAction =
|
|
140
|
+
const action = outranking(LISTING_ACTION_CLASS);
|
|
141
|
+
const cardAction = outranking(LISTING_CARD_ACTION_CLASS);
|
|
92
142
|
const row = `.${LISTING_ACTIONS_CLASS}`;
|
|
93
143
|
const over = `.${LISTING_ACTIONS_OVERLAY_CLASS}`;
|
|
94
144
|
const label = `.${LISTING_ACTION_LABEL_CLASS}`;
|
|
95
|
-
const hit = String(LISTING_ACTION_HIT);
|
|
96
145
|
const phone = `(max-width:${String(breakpoints.tablet - 1)}px)`;
|
|
97
146
|
return [
|
|
98
147
|
// The floor. `min-*` rather than `width`/`height`: a share button with a
|
|
@@ -100,14 +149,13 @@ export function actionRowCss(): string {
|
|
|
100
149
|
// `controlHeight` already reaches the height on a phone — this is the
|
|
101
150
|
// guarantee for every OTHER viewport and for a host-registered button
|
|
102
151
|
// that never read the antd token at all.
|
|
103
|
-
`${action}{
|
|
152
|
+
`${action}{${floor(LISTING_ACTION_HIT)};` +
|
|
104
153
|
`display:inline-flex;align-items:center;justify-content:center}`,
|
|
105
154
|
// A card's control: one tier smaller where there is a cursor, the same
|
|
106
155
|
// 44px where there is a thumb. See LISTING_CARD_ACTION_HIT.
|
|
107
|
-
`${cardAction}{
|
|
108
|
-
`min-block-size:${String(LISTING_CARD_ACTION_HIT)}px;` +
|
|
156
|
+
`${cardAction}{${floor(LISTING_CARD_ACTION_HIT)};` +
|
|
109
157
|
`display:inline-flex;align-items:center;justify-content:center}`,
|
|
110
|
-
`@media ${phone}{${cardAction}{
|
|
158
|
+
`@media ${phone}{${cardAction}{${floor(LISTING_ACTION_HIT)}}}`,
|
|
111
159
|
// The cluster. `align-items:flex-end` so a blocked heart's reason — the
|
|
112
160
|
// one thing here that can be two lines — stacks against the same edge
|
|
113
161
|
// instead of pushing the controls inwards (the arrangement
|