@shipload/item-renderer 1.0.0-next.25 → 1.0.0-next.27

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipload/item-renderer",
3
- "version": "1.0.0-next.25",
3
+ "version": "1.0.0-next.27",
4
4
  "description": "Deterministic SVG rendering for Shipload items",
5
5
  "homepage": "https://github.com/shipload/toolkit/tree/master/packages/item-renderer",
6
6
  "repository": {
@@ -45,7 +45,7 @@
45
45
  "fonts:copy": "bun run scripts/copy-fonts.ts"
46
46
  },
47
47
  "dependencies": {
48
- "@shipload/sdk": "^1.0.0-next.25",
48
+ "@shipload/sdk": "^1.0.0-next.27",
49
49
  "@wharfkit/antelope": "1.2.0"
50
50
  },
51
51
  "devDependencies": {
package/src/index.ts CHANGED
@@ -42,7 +42,7 @@ export {
42
42
  export type {ResourceIconInlineOpts, ResourceIconSvgOpts} from './primitives/resource-icon.ts'
43
43
 
44
44
  // Item cell templates
45
- export {renderItemCell, itemCellGroup} from './templates/item-cell.ts'
45
+ export {renderItemCell, itemCellGroup, abbreviateQuantity} from './templates/item-cell.ts'
46
46
  export type {ItemCellProps, ItemCellGroupProps} from './templates/item-cell.ts'
47
47
 
48
48
  // Social card template (1200x630 OG image)
@@ -9,6 +9,24 @@ export interface ItemCellProps {
9
9
  resolved: ResolvedItem
10
10
  quantity?: number
11
11
  size?: number
12
+ quantityColor?: string
13
+ quantityPrefix?: string
14
+ }
15
+
16
+ export function abbreviateQuantity(n: number): string {
17
+ const abs = Math.abs(n)
18
+ if (abs < 1000) return String(n)
19
+ const units = [
20
+ {v: 1e9, s: 'b'},
21
+ {v: 1e6, s: 'm'},
22
+ {v: 1e3, s: 'k'},
23
+ ]
24
+ for (const u of units) {
25
+ if (abs >= u.v) {
26
+ return (n / u.v).toFixed(1).replace(/\.0$/, '') + u.s
27
+ }
28
+ }
29
+ return String(n)
12
30
  }
13
31
 
14
32
  export interface ItemCellGroupProps extends ItemCellProps {
@@ -34,32 +52,47 @@ function cellInner(props: ItemCellProps): string {
34
52
  'stroke-width': 1.5,
35
53
  })
36
54
 
55
+ const qty = props.quantity ?? 0
56
+ const showQuantity = props.quantityPrefix ? qty >= 1 : qty > 1
57
+
37
58
  let content = ''
38
59
  if (props.resolved.abbreviation) {
39
- const iconCy = size * 0.45
40
- content = text({
41
- x: cx,
42
- y: iconCy,
43
- value: props.resolved.abbreviation,
44
- size: Math.round(size * 0.28),
45
- weight: 700,
46
- anchor: 'middle',
47
- color: tokens.colors.text.primary,
48
- family: tokens.typography.display,
49
- })
60
+ content = showQuantity
61
+ ? text({
62
+ x: cx,
63
+ y: size * 0.45,
64
+ value: props.resolved.abbreviation,
65
+ size: Math.round(size * 0.28),
66
+ weight: 700,
67
+ anchor: 'middle',
68
+ color: tokens.colors.text.primary,
69
+ family: tokens.typography.display,
70
+ })
71
+ : text({
72
+ x: cx,
73
+ y: height / 2,
74
+ value: props.resolved.abbreviation,
75
+ size: Math.round(size * 0.36),
76
+ weight: 700,
77
+ anchor: 'middle',
78
+ dominantBaseline: 'central',
79
+ color: tokens.colors.text.primary,
80
+ family: tokens.typography.display,
81
+ })
50
82
  } else if (props.resolved.category) {
51
- const iconSize = Math.round(size * 0.66)
83
+ const iconSize = Math.round(size * (showQuantity ? 0.66 : 0.84))
84
+ const iconY = showQuantity ? Math.round(size * 0.12) : Math.round(height / 2 - iconSize / 2)
52
85
  content = resourceIcon(props.resolved.category, {
53
86
  x: (size - iconSize) / 2,
54
- y: Math.round(size * 0.12),
87
+ y: iconY,
55
88
  size: iconSize,
56
89
  })
57
90
  } else if (props.resolved.icon) {
58
91
  content = text({
59
92
  x: cx,
60
- y: size * 0.4,
93
+ y: showQuantity ? size * 0.4 : height / 2,
61
94
  value: props.resolved.icon,
62
- size: Math.round(size * 0.44),
95
+ size: Math.round(size * (showQuantity ? 0.44 : 0.56)),
63
96
  weight: 400,
64
97
  anchor: 'middle',
65
98
  dominantBaseline: 'central',
@@ -67,21 +100,41 @@ function cellInner(props: ItemCellProps): string {
67
100
  })
68
101
  }
69
102
 
70
- const qty = props.quantity ?? 0
71
103
  let quantityText = ''
72
- if (qty > 1) {
73
- const qtyFontSize = Math.max(9, Math.round(size * 0.22))
74
- const qtyPad = Math.max(3, Math.round(size * 0.12))
75
- quantityText = text({
76
- x: size - qtyPad,
77
- y: height - qtyPad,
78
- value: String(qty),
79
- size: qtyFontSize,
80
- weight: 700,
81
- anchor: 'end',
82
- color: tokens.colors.text.primary,
83
- family: tokens.typography.display,
104
+ if (showQuantity) {
105
+ const label = (props.quantityPrefix ?? '') + abbreviateQuantity(qty)
106
+ const fontSize = Math.max(8, Math.round(size * 0.2))
107
+ const charW = fontSize * 0.6
108
+ const padX = Math.max(2, Math.round(fontSize * 0.34))
109
+ const padY = Math.max(1, Math.round(fontSize * 0.18))
110
+ const margin = Math.max(2, Math.round(size * 0.06))
111
+ const plateW = Math.round(label.length * charW) + padX * 2
112
+ const plateH = fontSize + padY * 2
113
+ const plateRight = size - margin
114
+ const plateBottom = height - margin
115
+ const plate = el('rect', {
116
+ x: plateRight - plateW,
117
+ y: plateBottom - plateH,
118
+ width: plateW,
119
+ height: plateH,
120
+ rx: Math.round(plateH / 2),
121
+ ry: Math.round(plateH / 2),
122
+ fill: '#050c24',
123
+ 'fill-opacity': 0.82,
84
124
  })
125
+ quantityText =
126
+ plate +
127
+ text({
128
+ x: plateRight - padX,
129
+ y: plateBottom - plateH / 2,
130
+ value: label,
131
+ size: fontSize,
132
+ weight: 700,
133
+ anchor: 'end',
134
+ dominantBaseline: 'central',
135
+ color: props.quantityColor ?? tokens.colors.text.primary,
136
+ family: tokens.typography.mono,
137
+ })
85
138
  }
86
139
 
87
140
  return border + content + quantityText