@submitium/badges 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/index.d.ts +8 -2
- package/index.js +2 -2
- package/{layout-DvNYYz0J.js → layout-cMiQ5rSW.js} +25 -11
- package/package.json +1 -1
- package/react.js +1 -1
- package/vue.js +1 -1
package/README.md
CHANGED
|
@@ -129,6 +129,7 @@ The format (`ribbon`: one gliding line in the footer bar; `window`: a small glid
|
|
|
129
129
|
- **Checked.** The wall must match a strict contract (`parseWall`): https links that point to the directory's own domain, https images, a closed list of `rel` values, plain text with a length limit, and a wall that hasn't expired (a week at most). Anything else and the whole wall is refused, not repaired.
|
|
130
130
|
- **Plain elements only.** The wall becomes links, images, a few spans and one scoped `<style>`, all written by this package from numbers (no CSS comes from the API). React, Vue, Svelte and Astro write each element themselves and escape every value.
|
|
131
131
|
- **In your HTML.** The badges are in the HTML your server sends, which is what directory checkers read. No client JavaScript: motion is pure CSS. It pauses on hover and keyboard focus, and stands still under `prefers-reduced-motion`.
|
|
132
|
+
- **Visible from day one.** Before your first order the wall has no badge yet: it shows one gliding line, "Directory badges will appear here as this product gets listed.", where the badges will be. Badges replace it as directories are ordered.
|
|
132
133
|
- **Never breaks your page.** If the API is slow, down or answers anything unexpected, the last good wall is kept, or nothing renders.
|
|
133
134
|
- **Fair to directories.** Each badge links to its directory with the directory's official `rel`, the image loads from the directory's own URL, and no badge is hidden or duplicated. Badges a directory wants in view at all times never move.
|
|
134
135
|
- **Attribution.** One discreet "Badge wall by Submitium" link, dofollow by default; you can make it nofollow in the dashboard.
|
package/index.d.ts
CHANGED
|
@@ -34,8 +34,14 @@ type WallNode = {
|
|
|
34
34
|
};
|
|
35
35
|
export declare const ATTRIBUTION_URL = "https://submitium.com/?ref=badge-wall";
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
37
|
+
* Before the first order the wall has no badge yet: one line says where they will be, gliding
|
|
38
|
+
* where the badges will glide, so the site owner sees the place it takes (founder's review,
|
|
39
|
+
* 2026-10-21). Visitors read it too: plain, true for anyone.
|
|
40
|
+
*/
|
|
41
|
+
export declare const EMPTY_TEXT = "Directory badges will appear here as this product gets listed.";
|
|
42
|
+
/**
|
|
43
|
+
* The wall as a tree. An empty wall (nothing ordered yet) holds one line where the badges will be
|
|
44
|
+
* (EMPTY_TEXT); our check finds it all the same, by the project's key on its root.
|
|
39
45
|
*/
|
|
40
46
|
export declare function layoutWall(wall: Wall): WallNode;
|
|
41
47
|
//#endregion
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as verifySignedWall, c as REL_TOKENS, d as WALL_VERSION, f as parseWall, i as isSignedWall, l as THEMES, n as fetchWall, o as ATTRIBUTIONS, r as SUBMITIUM_PUBLIC_KEYS, s as LIMITS, t as DEFAULT_API_URL, u as VARIANTS } from "./client-x4bD7iXW.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as TAGS, c as styleText, i as ATTRS, n as EMPTY_TEXT, o as attrsOf, r as layoutWall, s as cssOf, t as ATTRIBUTION_URL } from "./layout-cMiQ5rSW.js";
|
|
3
3
|
//#region src/html.ts
|
|
4
4
|
const VOID = /* @__PURE__ */ new Set([
|
|
5
5
|
"img",
|
|
@@ -45,4 +45,4 @@ function write(node, depth) {
|
|
|
45
45
|
return `<${node.tag}${attrs}>${inner}${lines ? end : ""}</${node.tag}>`;
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
48
|
-
export { ATTRIBUTIONS, ATTRIBUTION_URL, ATTRS, DEFAULT_API_URL, LIMITS, REL_TOKENS, SUBMITIUM_PUBLIC_KEYS, TAGS, THEMES, VARIANTS, WALL_VERSION, attrsOf, cssOf, fetchWall, isSignedWall, layoutWall, parseWall, styleText, verifySignedWall, wallToHtml };
|
|
48
|
+
export { ATTRIBUTIONS, ATTRIBUTION_URL, ATTRS, DEFAULT_API_URL, EMPTY_TEXT, LIMITS, REL_TOKENS, SUBMITIUM_PUBLIC_KEYS, TAGS, THEMES, VARIANTS, WALL_VERSION, attrsOf, cssOf, fetchWall, isSignedWall, layoutWall, parseWall, styleText, verifySignedWall, wallToHtml };
|
|
@@ -178,9 +178,16 @@ function windowFormat({ movers, badge }) {
|
|
|
178
178
|
moving: zone("sbm-w", movers.map((m, i) => item(loop.delays[i], badge(m))))
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Before the first order the wall has no badge yet: one line says where they will be, gliding
|
|
183
|
+
* where the badges will glide, so the site owner sees the place it takes (founder's review,
|
|
184
|
+
* 2026-10-21). Visitors read it too: plain, true for anyone.
|
|
185
|
+
*/
|
|
186
|
+
const EMPTY_TEXT = "Directory badges will appear here as this product gets listed.";
|
|
187
|
+
const CSS_EMPTY = `.sbm-ph{position:relative;flex:1 1 260px;width:260px;min-width:0;max-width:520px;height:${H}px;overflow:hidden;${mask(40)}}.sbm-col .sbm-ph{flex:none;width:320px;max-width:100%}.sbm-pt{display:flex;width:max-content;height:100%;align-items:center;animation:sbm-pe 18s linear infinite}.sbm-pt span{padding-right:48px;white-space:nowrap;opacity:.7}@keyframes sbm-pe{from{transform:translateX(0)}to{transform:translateX(-50%)}}.sbm:hover .sbm-pt,.sbm:focus-within .sbm-pt{animation-play-state:paused}@media (prefers-reduced-motion:reduce){.sbm-ph{width:auto;height:auto;${NO_MASK}}.sbm-pt{width:auto;animation:none}.sbm-pt span{white-space:normal;padding:0}.sbm-pt span+span{display:none}}`;
|
|
181
188
|
const LAYOUT_CSS = {
|
|
182
|
-
ribbon: ".sbm-ribbon{container-type:inline-size}.sbm-bar{display:flex;align-items:center;flex-wrap:wrap;gap:10px 16px;font-size:12px}@container (max-width:520px){.sbm-lb{display:none}.sbm-w{flex-basis:100%}}",
|
|
183
|
-
window: ".sbm-col{display:flex;flex-direction:column;align-items:flex-start;gap:10px;font-size:12px}"
|
|
189
|
+
ribbon: ".sbm-ribbon{container-type:inline-size;width:100%;min-width:0}.sbm-bar{display:flex;align-items:center;flex-wrap:wrap;gap:10px 16px;font-size:12px}@container (max-width:520px){.sbm-lb{display:none}.sbm-w{flex-basis:100%}}",
|
|
190
|
+
window: ".sbm-window{min-width:0;max-width:100%}.sbm-col{display:flex;flex-direction:column;align-items:flex-start;gap:10px;font-size:12px}"
|
|
184
191
|
};
|
|
185
192
|
const MARK_PATHS = [["#4432F4", "M20 1l-1 1 0 2 6 27 1 1 2 0 1 0 6-12 12-2 1-2-1-1-11-6-14-8-2 0zM39 22l-2 0-7 11-2 1-2 0-2-1-3-11-14 8-1 1 2 2 20 10 3 1 2 0 23-11 2-2-1-1-15-9-3 1z"], ["#CAC9FD", "M11 37l-5 3 0 1 1 0 25 13 2-1 18-9 5-2 1-2-1-1-5-2-19 9-1 1-21-10zM10 47l-4 1 0 2 1 1 25 12 2 0 9-5 13-7 2-1 0-2-2 0-4-2-17 9-3 1-20-10-2 1z"]];
|
|
186
193
|
function attributionNode(rel) {
|
|
@@ -250,8 +257,8 @@ function scoped(css, scope, root) {
|
|
|
250
257
|
return out;
|
|
251
258
|
}
|
|
252
259
|
/**
|
|
253
|
-
* The wall as a tree. An empty wall (nothing ordered yet)
|
|
254
|
-
*
|
|
260
|
+
* The wall as a tree. An empty wall (nothing ordered yet) holds one line where the badges will be
|
|
261
|
+
* (EMPTY_TEXT); our check finds it all the same, by the project's key on its root.
|
|
255
262
|
*/
|
|
256
263
|
function layoutWall(wall) {
|
|
257
264
|
const { variant, theme, attribution } = wall.settings;
|
|
@@ -266,7 +273,18 @@ function layoutWall(wall) {
|
|
|
266
273
|
"data-submitium": wall.key,
|
|
267
274
|
"data-submitium-variant": variant
|
|
268
275
|
}, children);
|
|
269
|
-
|
|
276
|
+
const by = attribution === "none" ? null : attributionNode(attribution);
|
|
277
|
+
const some = (...nodes) => nodes.filter((n) => n !== null);
|
|
278
|
+
const styleOf = (rules) => {
|
|
279
|
+
scope = `sbm-x${hash(rules)}`;
|
|
280
|
+
return el("style", {}, [scoped(rules, `.${scope}`, new Set(classes))]);
|
|
281
|
+
};
|
|
282
|
+
if (wall.badges.length === 0) {
|
|
283
|
+
const css = styleOf(CSS_BASE + (by ? CSS_BY : "") + LAYOUT_CSS[variant] + CSS_EMPTY);
|
|
284
|
+
const line = el("div", { class: "sbm-ph" }, [el("div", { class: "sbm-pt" }, [el("span", {}, [EMPTY_TEXT]), el("span", { "aria-hidden": "true" }, [EMPTY_TEXT])])]);
|
|
285
|
+
if (variant === "window") return root([css, el("div", { class: "sbm-col" }, some(line, by))]);
|
|
286
|
+
return root([css, el("div", { class: "sbm-bar" }, some(line, by ? el("span", { class: "sbm-ctl" }, [by]) : null))]);
|
|
287
|
+
}
|
|
270
288
|
const candidates = wall.badges.filter((b) => !b.still).map((b) => ({
|
|
271
289
|
badge: b,
|
|
272
290
|
width: widthOf(b)
|
|
@@ -282,16 +300,12 @@ function layoutWall(wall) {
|
|
|
282
300
|
class: "sbm-fix",
|
|
283
301
|
"data-submitium-zone": "fixed"
|
|
284
302
|
}, still.map((b) => el("span", { class: "sbm-i" }, [badgeNode(b, theme, false)]))) : null;
|
|
285
|
-
const by = attribution === "none" ? null : attributionNode(attribution);
|
|
286
303
|
const ctl = by ? el("span", { class: "sbm-ctl" }, [by]) : null;
|
|
287
304
|
const zoneNode = built.moving;
|
|
288
305
|
const label = el("span", { class: "sbm-lb" }, ["Featured on"]);
|
|
289
|
-
const
|
|
290
|
-
scope = `sbm-x${hash(rules)}`;
|
|
291
|
-
const css = el("style", {}, [scoped(rules, `.${scope}`, new Set(classes))]);
|
|
292
|
-
const some = (...nodes) => nodes.filter((n) => n !== null);
|
|
306
|
+
const css = styleOf(CSS_BASE + (by ? CSS_BY : "") + LAYOUT_CSS[variant] + built.css);
|
|
293
307
|
if (variant === "window") return root([css, el("div", { class: "sbm-col" }, some(zoneNode, fixed, by))]);
|
|
294
308
|
return zoneNode ? root(some(css, el("div", { class: "sbm-bar" }, some(label, zoneNode, ctl)), fixed)) : root([css, el("div", { class: "sbm-bar" }, some(label, fixed, ctl))]);
|
|
295
309
|
}
|
|
296
310
|
//#endregion
|
|
297
|
-
export {
|
|
311
|
+
export { TAGS as a, styleText as c, ATTRS as i, EMPTY_TEXT as n, attrsOf as o, layoutWall as r, cssOf as s, ATTRIBUTION_URL as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@submitium/badges",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "The Submitium badge wall: a signed, checked wall rendered as plain links and images. React, Vue, Svelte, Astro.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Submitium <contact@submitium.com>",
|
package/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as fetchWall } from "./client-x4bD7iXW.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as TAGS, o as attrsOf, r as layoutWall, s as cssOf } from "./layout-cMiQ5rSW.js";
|
|
3
3
|
import { createElement } from "react";
|
|
4
4
|
//#region src/react.tsx
|
|
5
5
|
/** React's names for the attributes React spells differently. */
|
package/vue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as TAGS, o as attrsOf, r as layoutWall, s as cssOf } from "./layout-cMiQ5rSW.js";
|
|
2
2
|
import { defineComponent, h } from "vue";
|
|
3
3
|
//#region src/vue.ts
|
|
4
4
|
function toVue(node) {
|