unicon 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +31 -0
- data/LICENSE.txt +21 -0
- data/README.md +280 -0
- data/lib/unicon/icons/actions/editing.rb +22 -0
- data/lib/unicon/icons/actions/playing.rb +11 -0
- data/lib/unicon/icons/actions/pointing.rb +22 -0
- data/lib/unicon/icons/actions/sending.rb +10 -0
- data/lib/unicon/icons/actions/switching.rb +16 -0
- data/lib/unicon/icons/aliases.rb +40 -0
- data/lib/unicon/icons/charts.rb +11 -0
- data/lib/unicon/icons/commerce.rb +18 -0
- data/lib/unicon/icons/communication.rb +13 -0
- data/lib/unicon/icons/devices.rb +13 -0
- data/lib/unicon/icons/files.rb +15 -0
- data/lib/unicon/icons/layout.rb +11 -0
- data/lib/unicon/icons/media.rb +16 -0
- data/lib/unicon/icons/nature.rb +13 -0
- data/lib/unicon/icons/people.rb +10 -0
- data/lib/unicon/icons/places.rb +16 -0
- data/lib/unicon/icons/security.rb +10 -0
- data/lib/unicon/icons/status.rb +24 -0
- data/lib/unicon/icons/time.rb +13 -0
- data/lib/unicon/icons/tools.rb +14 -0
- data/lib/unicon/icons/transport.rb +11 -0
- data/lib/unicon/version.rb +4 -0
- data/lib/unicon.rb +71 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dc989c8399dcf0493ce84db4d26fd608e08ce9ae81dc866753a92077050c9aa8
|
|
4
|
+
data.tar.gz: f7eeb22b2579e4c5863a452d708990dfe802c82ca2fdb646ae6e24216241f356
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a8c3f33973c31908f92d6e223d662634ca9421574d05e08e348c346cb374c76496c64673bea6eaa2ffd44426865534b6e5d0810f7346818f8530fc43979e6791
|
|
7
|
+
data.tar.gz: 051a0ec37475501377a062b7614ddee1ab9f45901a98f0329932cb573e99d597a64eb0f528ff771dbb667eb0612350384c8e7f1b6610613f0c3c5ea962f4de83
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every entry says whether it is a fix, a feature or a breaking change, since that is
|
|
4
|
+
what decides both whether a reader can take it and what the next version number is.
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
- **Fix.** 119 of the 126 SF Symbols the table draws are vendored as SVG rather than as a
|
|
9
|
+
20-pixel PNG, so they stay sharp at any size and take the colour around them. Seven are
|
|
10
|
+
still PNGs: six the tracer would not commit to a match on, and `receipt`, which this
|
|
11
|
+
macOS cannot draw at all.
|
|
12
|
+
|
|
13
|
+
## [0.1.0] - 2026-08-05
|
|
14
|
+
|
|
15
|
+
- **Feature.** One concept, three icon names. `Unicon.fetch :house` — or `Unicon[:house]`
|
|
16
|
+
— answers `{ bootstrap: 'house', ios: 'house', android: 'home' }`: symbol keys, frozen
|
|
17
|
+
string names. An app says what it means once and the web page, the iOS client and the
|
|
18
|
+
Android client each draw it from the set they already ship. A name nothing is known by
|
|
19
|
+
answers the circle rather than nil, so a caller that renders whatever it is handed
|
|
20
|
+
cannot draw a blank.
|
|
21
|
+
- **Feature.** 169 concepts, listed only where Bootstrap Icons, SF Symbols and Material
|
|
22
|
+
Symbols all have a sensible icon, and 134 model names — `:user`, `:invoice`, `:zip` —
|
|
23
|
+
resolving to the concept they borrow from.
|
|
24
|
+
- **Feature.** Three lists to ask for. `Unicon.icons` is the 127 distinct pictures, one
|
|
25
|
+
name each, for offering somebody a choice. `Unicon.names` is all 303 names that
|
|
26
|
+
resolve, for checking a name against. `Unicon.actions` is the 42 doing words —
|
|
27
|
+
`close`, `pencil`, every chevron — which a toolbar draws and no picker should offer.
|
|
28
|
+
- **Feature.** `rake validate` checks all 909 names against the list each design system
|
|
29
|
+
publishes, vendored under `data/`, and the default `rake` task runs it. An invented
|
|
30
|
+
name renders as an empty box in an app and passes every other kind of test, so this is
|
|
31
|
+
the check the gem exists for.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 claudiob
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Unicon
|
|
2
|
+
|
|
3
|
+
One concept, three icon names.
|
|
4
|
+
|
|
5
|
+
An app knows it wants to draw a house. What a house is *called* depends on who is
|
|
6
|
+
drawing it: Bootstrap Icons says `house`, SF Symbols says `house`, Material Symbols
|
|
7
|
+
says `home`. Unicon holds that translation, so a Rails app can name the concept once
|
|
8
|
+
and let the web page, the iOS client and the Android client each draw it from the set
|
|
9
|
+
they already ship.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
Unicon.fetch :house
|
|
13
|
+
# => { bootstrap: 'house', ios: 'house', android: 'home' }
|
|
14
|
+
|
|
15
|
+
Unicon[:contact]
|
|
16
|
+
# => { bootstrap: 'person-rolodex', ios: 'person.crop.circle', android: 'contacts' }
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`Unicon[]` and `Unicon.fetch` are the same method; `[]` is the shorter spelling.
|
|
20
|
+
|
|
21
|
+
The keys are symbols and the names are frozen strings: a name is on its way into
|
|
22
|
+
`"bi bi-#{icon}"` or into JSON either way, and `person.crop.circle` is a string long
|
|
23
|
+
before it is a symbol.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
gem 'unicon'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## The three keys
|
|
32
|
+
|
|
33
|
+
Every concept carries exactly three keys, and never fewer:
|
|
34
|
+
|
|
35
|
+
| Key | Design system | Licence |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `:bootstrap` | [Bootstrap Icons](https://icons.getbootstrap.com) | MIT |
|
|
38
|
+
| `:ios` | [SF Symbols](https://developer.apple.com/sf-symbols/) | Apple, Apple platforms only |
|
|
39
|
+
| `:android` | [Material Symbols](https://fonts.google.com/icons) | Apache 2.0 |
|
|
40
|
+
|
|
41
|
+
The keys are named after the client that draws the icon rather than after the system,
|
|
42
|
+
because that is the question a caller is answering. `:ios` always holds an SF Symbols
|
|
43
|
+
name and `:android` always holds a Material Symbols name — nothing else is meant by
|
|
44
|
+
them.
|
|
45
|
+
|
|
46
|
+
## Anything unknown draws a circle
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
Unicon.fetch :nothing_by_this_name
|
|
50
|
+
# => { bootstrap: 'circle', ios: 'circle', android: 'circle' }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A caller rendering whatever it is handed never has to check first, and never draws a
|
|
54
|
+
blank. A concept may be given as a string, so `Unicon['contact']` works too.
|
|
55
|
+
|
|
56
|
+
## Three lists, and which one you want
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
Unicon.icons # => [:account, :airplane, :alarm, :archive, :award, :badge, ...] 127
|
|
60
|
+
Unicon.names # => [:account, :address, :agent, :airplane, :alarm, :alert, ...] 303
|
|
61
|
+
Unicon.actions # => [:arrow_down, :arrow_left, :arrow_right, :arrow_up, ...] 42
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- `Unicon.icons` is **one name per distinct picture**. No two entries draw the same
|
|
65
|
+
thing, which is what makes it the list to show somebody choosing an icon.
|
|
66
|
+
- `Unicon.names` is **the union of all three groups** — the icons, the actions and the
|
|
67
|
+
model synonyms — and so every name that resolves. Ask for it to find out whether a
|
|
68
|
+
name draws something; ask for `icons` when somebody is choosing one.
|
|
69
|
+
- `Unicon.actions` is **the doing words**: `close`, `pencil`, every chevron. A toolbar
|
|
70
|
+
needs them and `Unicon[:close]` answers, but nobody has a Close model, so `icons`
|
|
71
|
+
leaves them out on purpose.
|
|
72
|
+
|
|
73
|
+
All three are frozen, sorted arrays of symbols; only what `fetch` returns is strings.
|
|
74
|
+
`Unicon::ICONS`, `Unicon::TABLE` and `Unicon::ACTIONS` are the tables behind them, for a
|
|
75
|
+
caller who wants the pictures as well as the names.
|
|
76
|
+
|
|
77
|
+
## Model names
|
|
78
|
+
|
|
79
|
+
A host app looks an icon up by the name of the model it is rendering, and the more names
|
|
80
|
+
answer, the less any app has to declare. 134 of them borrow the icons of a concept —
|
|
81
|
+
`:user` takes the person, `:zip` takes the pin — and the Aliases column of the table below
|
|
82
|
+
says which took which. They resolve without swelling the list a picker offers.
|
|
83
|
+
|
|
84
|
+
Four borrow an action rather than a thing, so they are the four the table cannot show:
|
|
85
|
+
`:export` takes the download, `:import` the upload, `:sync` the refresh, `:subscription`
|
|
86
|
+
the repeat. Each still answers to `Unicon[]` like any other name.
|
|
87
|
+
|
|
88
|
+
## Every concept
|
|
89
|
+
|
|
90
|
+
A concept is listed only when all three systems have a sensible icon for it. Where one
|
|
91
|
+
of the three has none, the concept is left out rather than filled in with something
|
|
92
|
+
close — an agent is a badge rather than a robot here, because Bootstrap and Material
|
|
93
|
+
both draw a robot and SF Symbols has none.
|
|
94
|
+
|
|
95
|
+
The SF Symbols are shown under section 2.11 of the Xcode and Apple SDKs Agreement, which
|
|
96
|
+
licenses them for developing applications for Apple-branded products, and this repository
|
|
97
|
+
is part of building one. They may not be redistributed for any other purpose, so anyone
|
|
98
|
+
reusing this gem outside that context should show the names alone.
|
|
99
|
+
|
|
100
|
+
The cells hold the icon and not its name, so the table stays readable at 127 rows. **The
|
|
101
|
+
name each system uses is on the icon**: hover it, or reach it with a screen reader, and
|
|
102
|
+
`Unicon[:search][:ios]` reads back as `magnifyingglass`.
|
|
103
|
+
|
|
104
|
+
The 42 action icons are not in the table. They are fetchable — `Unicon[:pencil]` answers
|
|
105
|
+
— but they name what an app does rather than what it has, and this table is `Unicon.icons`
|
|
106
|
+
drawn out. `Unicon.actions` names them all.
|
|
107
|
+
|
|
108
|
+
<!-- concepts:start -->
|
|
109
|
+
| Icon | Aliases | Bootstrap | iOS | Android |
|
|
110
|
+
| --- | --- | --- | --- | --- |
|
|
111
|
+
| `:account` | `:avatar` `:profile` |  |  |  |
|
|
112
|
+
| `:airplane` | |  |  |  |
|
|
113
|
+
| `:alarm` | `:deadline` |  |  |  |
|
|
114
|
+
| `:archive` | |  |  |  |
|
|
115
|
+
| `:award` | `:specialty` |  |  |  |
|
|
116
|
+
| `:badge` | `:agent` `:employee` `:role` |  |  |  |
|
|
117
|
+
| `:bag` | |  |  |  |
|
|
118
|
+
| `:bank` | |  |  |  |
|
|
119
|
+
| `:bar_chart` | `:vertical` |  |  |  |
|
|
120
|
+
| `:barcode` | |  |  |  |
|
|
121
|
+
| `:battery` | |  |  |  |
|
|
122
|
+
| `:bell` | `:notification` `:reminder` |  |  |  |
|
|
123
|
+
| `:bicycle` | |  |  |  |
|
|
124
|
+
| `:bolt` | |  |  |  |
|
|
125
|
+
| `:book` | |  |  |  |
|
|
126
|
+
| `:bookmark` | |  |  |  |
|
|
127
|
+
| `:box` | `:inventory` `:item` `:package` `:product` |  |  |  |
|
|
128
|
+
| `:briefcase` | `:provider` |  |  |  |
|
|
129
|
+
| `:brush` | |  |  |  |
|
|
130
|
+
| `:building` | `:brand` `:business` `:city` `:company` `:office` `:organization` |  |  |  |
|
|
131
|
+
| `:bus` | |  |  |  |
|
|
132
|
+
| `:calendar` | `:event` `:plan` `:schedule` |  |  |  |
|
|
133
|
+
| `:calendar_check` | `:appointment` `:booking` |  |  |  |
|
|
134
|
+
| `:camera` | |  |  |  |
|
|
135
|
+
| `:car` | `:ride` `:vehicle` |  |  |  |
|
|
136
|
+
| `:cart` | `:order` `:purchase` |  |  |  |
|
|
137
|
+
| `:cash` | `:cost` |  |  |  |
|
|
138
|
+
| `:chat` | `:comment` `:conversation` `:message` |  |  |  |
|
|
139
|
+
| `:check_circle` | |  |  |  |
|
|
140
|
+
| `:circle` | |  |  |  |
|
|
141
|
+
| `:clipboard` | `:assessment` `:survey` |  |  |  |
|
|
142
|
+
| `:clock` | `:history` `:session` `:shift` |  |  |  |
|
|
143
|
+
| `:close_circle` | |  |  |  |
|
|
144
|
+
| `:cloud` | `:backup` |  |  |  |
|
|
145
|
+
| `:compass` | |  |  |  |
|
|
146
|
+
| `:contact` | |  |  |  |
|
|
147
|
+
| `:credit_card` | `:charge` `:payment` `:transaction` |  |  |  |
|
|
148
|
+
| `:display` | `:screen` |  |  |  |
|
|
149
|
+
| `:document` | `:file` `:form` `:page` `:summary` |  |  |  |
|
|
150
|
+
| `:droplet` | |  |  |  |
|
|
151
|
+
| `:envelope` | `:email` |  |  |  |
|
|
152
|
+
| `:eye` | `:view` |  |  |  |
|
|
153
|
+
| `:filter` | |  |  |  |
|
|
154
|
+
| `:fingerprint` | |  |  |  |
|
|
155
|
+
| `:fire` | |  |  |  |
|
|
156
|
+
| `:flag` | `:milestone` |  |  |  |
|
|
157
|
+
| `:folder` | `:project` |  |  |  |
|
|
158
|
+
| `:gear` | `:configuration` `:setting` |  |  |  |
|
|
159
|
+
| `:gift` | `:coupon` `:offer` |  |  |  |
|
|
160
|
+
| `:globe` | `:country` `:site` |  |  |  |
|
|
161
|
+
| `:grid` | |  |  |  |
|
|
162
|
+
| `:hammer` | `:job` |  |  |  |
|
|
163
|
+
| `:headphones` | |  |  |  |
|
|
164
|
+
| `:heart` | |  |  |  |
|
|
165
|
+
| `:hospital` | |  |  |  |
|
|
166
|
+
| `:hourglass` | |  |  |  |
|
|
167
|
+
| `:house` | `:home` |  |  |  |
|
|
168
|
+
| `:image` | `:banner` `:photo` `:picture` |  |  |  |
|
|
169
|
+
| `:inbox` | |  |  |  |
|
|
170
|
+
| `:infinity` | |  |  |  |
|
|
171
|
+
| `:info` | |  |  |  |
|
|
172
|
+
| `:invoice` | `:bill` |  |  |  |
|
|
173
|
+
| `:key` | `:credential` `:permission` `:token` |  |  |  |
|
|
174
|
+
| `:keyboard` | |  |  |  |
|
|
175
|
+
| `:laptop` | |  |  |  |
|
|
176
|
+
| `:lightbulb` | |  |  |  |
|
|
177
|
+
| `:link` | |  |  |  |
|
|
178
|
+
| `:list` | `:log` |  |  |  |
|
|
179
|
+
| `:lock` | `:password` |  |  |  |
|
|
180
|
+
| `:map` | `:area` `:county` `:market` `:region` `:state` `:territory` `:trip` |  |  |  |
|
|
181
|
+
| `:megaphone` | `:announcement` `:campaign` |  |  |  |
|
|
182
|
+
| `:microphone` | `:recording` |  |  |  |
|
|
183
|
+
| `:mobile` | |  |  |  |
|
|
184
|
+
| `:moon` | |  |  |  |
|
|
185
|
+
| `:music` | `:track` |  |  |  |
|
|
186
|
+
| `:note` | `:memo` |  |  |  |
|
|
187
|
+
| `:palette` | |  |  |  |
|
|
188
|
+
| `:paperclip` | `:attachment` |  |  |  |
|
|
189
|
+
| `:people` | `:group` `:staff` `:team` |  |  |  |
|
|
190
|
+
| `:percent` | `:discount` |  |  |  |
|
|
191
|
+
| `:person` | `:client` `:customer` `:lead` `:member` `:user` |  |  |  |
|
|
192
|
+
| `:phone` | `:call` |  |  |  |
|
|
193
|
+
| `:pie_chart` | |  |  |  |
|
|
194
|
+
| `:pin` | `:address` `:destination` `:location` `:zip` |  |  |  |
|
|
195
|
+
| `:playlist` | `:episode` |  |  |  |
|
|
196
|
+
| `:puzzle` | `:integration` `:platform` `:plugin` |  |  |  |
|
|
197
|
+
| `:qr_code` | |  |  |  |
|
|
198
|
+
| `:question` | `:answer` |  |  |  |
|
|
199
|
+
| `:rain` | |  |  |  |
|
|
200
|
+
| `:receipt` | |  |  |  |
|
|
201
|
+
| `:reply` | |  |  |  |
|
|
202
|
+
| `:ruler` | |  |  |  |
|
|
203
|
+
| `:search` | |  |  |  |
|
|
204
|
+
| `:server` | |  |  |  |
|
|
205
|
+
| `:shield` | |  |  |  |
|
|
206
|
+
| `:shop` | `:franchise` `:vendor` |  |  |  |
|
|
207
|
+
| `:signature` | `:contract` |  |  |  |
|
|
208
|
+
| `:signpost` | `:route` `:source` |  |  |  |
|
|
209
|
+
| `:sliders` | `:optimization` `:preference` |  |  |  |
|
|
210
|
+
| `:smiley` | `:feedback` |  |  |  |
|
|
211
|
+
| `:snow` | |  |  |  |
|
|
212
|
+
| `:speedometer` | `:dashboard` `:evaluation` |  |  |  |
|
|
213
|
+
| `:square` | |  |  |  |
|
|
214
|
+
| `:star` | `:rating` `:review` |  |  |  |
|
|
215
|
+
| `:stopwatch` | |  |  |  |
|
|
216
|
+
| `:sun` | |  |  |  |
|
|
217
|
+
| `:table` | |  |  |  |
|
|
218
|
+
| `:tag` | `:category` `:label` `:price` |  |  |  |
|
|
219
|
+
| `:task` | `:todo` |  |  |  |
|
|
220
|
+
| `:terminal` | `:api` `:prompt` |  |  |  |
|
|
221
|
+
| `:thumbs_down` | |  |  |  |
|
|
222
|
+
| `:thumbs_up` | |  |  |  |
|
|
223
|
+
| `:ticket` | |  |  |  |
|
|
224
|
+
| `:toolbox` | |  |  |  |
|
|
225
|
+
| `:train` | |  |  |  |
|
|
226
|
+
| `:tree` | |  |  |  |
|
|
227
|
+
| `:trending_up` | |  |  |  |
|
|
228
|
+
| `:trophy` | `:goal` |  |  |  |
|
|
229
|
+
| `:truck` | `:delivery` `:fleet` `:shipment` `:supplier` |  |  |  |
|
|
230
|
+
| `:video` | `:movie` |  |  |  |
|
|
231
|
+
| `:volume` | |  |  |  |
|
|
232
|
+
| `:wallet` | |  |  |  |
|
|
233
|
+
| `:warning` | `:alert` `:issue` |  |  |  |
|
|
234
|
+
| `:waveform` | `:audio` `:echo` |  |  |  |
|
|
235
|
+
| `:wifi` | |  |  |  |
|
|
236
|
+
| `:window` | `:app` |  |  |  |
|
|
237
|
+
| `:wrench` | |  |  |  |
|
|
238
|
+
|
|
239
|
+
A dashed circle is a symbol newer than the macOS that drew this table, which knows the SF Symbols 5 set. Hover it for the name, which resolves in an app built against a newer SDK.
|
|
240
|
+
<!-- concepts:end -->
|
|
241
|
+
|
|
242
|
+
## Where the names come from
|
|
243
|
+
|
|
244
|
+
Every name in the table is checked, by `rake validate`, against the list its own design
|
|
245
|
+
system publishes. The lists are vendored under `data/`, so the check needs no network
|
|
246
|
+
and runs as part of the default `rake` task.
|
|
247
|
+
|
|
248
|
+
- `data/bootstrap-icons.txt` — 2,078 names, from `font/bootstrap-icons.json` at tag
|
|
249
|
+
`v1.13.1` of [twbs/icons](https://github.com/twbs/icons).
|
|
250
|
+
- `data/material-symbols.txt` — 3,896 names, from Google's
|
|
251
|
+
[metadata endpoint](https://fonts.google.com/metadata/icons), keeping only what
|
|
252
|
+
Material Symbols Outlined actually ships. The endpoint also lists legacy Material
|
|
253
|
+
Icons, such as `laptop` and `smartphone`, which no longer resolve as symbols.
|
|
254
|
+
- `data/sf-symbols.txt` — 8,295 names, from `Metadata/name_availability.plist` inside
|
|
255
|
+
`SF Symbols.app`, filtered to names whose iOS availability is 18.3 or earlier. The
|
|
256
|
+
plist gives each release an iOS version, and that version is the constraint, so the
|
|
257
|
+
filter reads it rather than the release year. A name added in iOS 18.4 or later is
|
|
258
|
+
left out and cannot enter the table.
|
|
259
|
+
|
|
260
|
+
## The artwork in the table
|
|
261
|
+
|
|
262
|
+
All three columns show artwork vendored under `vendor/`, so the table renders whether or
|
|
263
|
+
not a CDN answers, and each directory carries the licence its artwork came with.
|
|
264
|
+
|
|
265
|
+
Bootstrap Icons and Material Symbols are downloaded from their published sources, and
|
|
266
|
+
differ from upstream in two attributes: a grey fill, so a row reads on a light and a dark
|
|
267
|
+
theme alike, and a 20px box, so the columns line up. Apple publishes no files to fetch,
|
|
268
|
+
so `tools/sf_symbols.swift` asks macOS to draw each symbol and writes a PNG in the same
|
|
269
|
+
grey and the same box.
|
|
270
|
+
|
|
271
|
+
That rendering is done by the machine running `rake readme`, and a machine only knows the
|
|
272
|
+
symbols its own system shipped with. Anything newer gets `assets/undrawn.svg` — a dashed
|
|
273
|
+
circle of our own drawing, not Apple's — with the symbol's name still on it to hover.
|
|
274
|
+
One row is in that state today: `:receipt`, whose SF symbol arrived in iOS 18.2, drawn on
|
|
275
|
+
a Mac running macOS 14.3. `rake validate` has already proved every name real, which is
|
|
276
|
+
why a symbol this Mac cannot draw is reported rather than fatal.
|
|
277
|
+
|
|
278
|
+
## Licence
|
|
279
|
+
|
|
280
|
+
MIT. See `LICENSE.txt`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Changing a record, or what is shown of it
|
|
3
|
+
EDITING = {
|
|
4
|
+
plus: { bootstrap: 'plus', ios: 'plus', android: 'add' },
|
|
5
|
+
minus: { bootstrap: 'dash', ios: 'minus', android: 'remove' },
|
|
6
|
+
check: { bootstrap: 'check', ios: 'checkmark', android: 'check' },
|
|
7
|
+
close: { bootstrap: 'x', ios: 'xmark', android: 'close' },
|
|
8
|
+
pencil: { bootstrap: 'pencil', ios: 'pencil', android: 'edit' },
|
|
9
|
+
trash: { bootstrap: 'trash', ios: 'trash', android: 'delete' },
|
|
10
|
+
copy: { bootstrap: 'copy', ios: 'doc.on.doc', android: 'content_copy' },
|
|
11
|
+
scissors: { bootstrap: 'scissors', ios: 'scissors', android: 'content_cut' },
|
|
12
|
+
undo: { bootstrap: 'arrow-counterclockwise', ios: 'arrow.uturn.backward', android: 'undo' },
|
|
13
|
+
refresh: { bootstrap: 'arrow-clockwise', ios: 'arrow.clockwise', android: 'refresh' },
|
|
14
|
+
sort: { bootstrap: 'sort-down', ios: 'arrow.up.arrow.down', android: 'sort' },
|
|
15
|
+
zoom_in: { bootstrap: 'zoom-in', ios: 'plus.magnifyingglass', android: 'zoom_in' },
|
|
16
|
+
zoom_out: { bootstrap: 'zoom-out', ios: 'minus.magnifyingglass', android: 'zoom_out' },
|
|
17
|
+
person_add: { bootstrap: 'person-plus', ios: 'person.badge.plus', android: 'person_add' },
|
|
18
|
+
person_remove: {
|
|
19
|
+
bootstrap: 'person-dash', ios: 'person.badge.minus', android: 'person_remove',
|
|
20
|
+
},
|
|
21
|
+
}.freeze
|
|
22
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Running a recording, and how it repeats
|
|
3
|
+
PLAYING = {
|
|
4
|
+
play: { bootstrap: 'play', ios: 'play', android: 'play_arrow' },
|
|
5
|
+
pause: { bootstrap: 'pause', ios: 'pause', android: 'pause' },
|
|
6
|
+
stop: { bootstrap: 'stop', ios: 'stop', android: 'stop' },
|
|
7
|
+
skip_next: { bootstrap: 'skip-forward', ios: 'forward.end', android: 'skip_next' },
|
|
8
|
+
shuffle: { bootstrap: 'shuffle', ios: 'shuffle', android: 'shuffle' },
|
|
9
|
+
repeat: { bootstrap: 'repeat', ios: 'repeat', android: 'repeat' },
|
|
10
|
+
}.freeze
|
|
11
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Where a control leads, and what it opens
|
|
3
|
+
POINTING = {
|
|
4
|
+
chevron_up: { bootstrap: 'chevron-up', ios: 'chevron.up', android: 'keyboard_arrow_up' },
|
|
5
|
+
chevron_down: {
|
|
6
|
+
bootstrap: 'chevron-down', ios: 'chevron.down', android: 'keyboard_arrow_down',
|
|
7
|
+
},
|
|
8
|
+
chevron_left: {
|
|
9
|
+
bootstrap: 'chevron-left', ios: 'chevron.left', android: 'keyboard_arrow_left',
|
|
10
|
+
},
|
|
11
|
+
chevron_right: {
|
|
12
|
+
bootstrap: 'chevron-right', ios: 'chevron.right', android: 'keyboard_arrow_right',
|
|
13
|
+
},
|
|
14
|
+
arrow_up: { bootstrap: 'arrow-up', ios: 'arrow.up', android: 'arrow_upward' },
|
|
15
|
+
arrow_down: { bootstrap: 'arrow-down', ios: 'arrow.down', android: 'arrow_downward' },
|
|
16
|
+
arrow_left: { bootstrap: 'arrow-left', ios: 'arrow.left', android: 'arrow_back' },
|
|
17
|
+
arrow_right: { bootstrap: 'arrow-right', ios: 'arrow.right', android: 'arrow_forward' },
|
|
18
|
+
menu: { bootstrap: 'list', ios: 'line.3.horizontal', android: 'menu' },
|
|
19
|
+
ellipsis: { bootstrap: 'three-dots', ios: 'ellipsis', android: 'more_horiz' },
|
|
20
|
+
sidebar: { bootstrap: 'layout-sidebar', ios: 'sidebar.left', android: 'view_sidebar' },
|
|
21
|
+
}.freeze
|
|
22
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Moving something out of the app, or into it
|
|
3
|
+
SENDING = {
|
|
4
|
+
send: { bootstrap: 'send', ios: 'paperplane', android: 'send' },
|
|
5
|
+
share: { bootstrap: 'share', ios: 'square.and.arrow.up', android: 'share' },
|
|
6
|
+
download: { bootstrap: 'download', ios: 'arrow.down.circle', android: 'download' },
|
|
7
|
+
upload: { bootstrap: 'upload', ios: 'arrow.up.circle', android: 'upload' },
|
|
8
|
+
printer: { bootstrap: 'printer', ios: 'printer', android: 'print' },
|
|
9
|
+
}.freeze
|
|
10
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Turning something on, off, or open
|
|
3
|
+
SWITCHING = {
|
|
4
|
+
login: {
|
|
5
|
+
bootstrap: 'box-arrow-in-right',
|
|
6
|
+
ios: 'rectangle.portrait.and.arrow.forward',
|
|
7
|
+
android: 'login',
|
|
8
|
+
},
|
|
9
|
+
logout: {
|
|
10
|
+
bootstrap: 'box-arrow-right', ios: 'rectangle.portrait.and.arrow.right', android: 'logout',
|
|
11
|
+
},
|
|
12
|
+
unlock: { bootstrap: 'unlock', ios: 'lock.open', android: 'lock_open' },
|
|
13
|
+
eye_slash: { bootstrap: 'eye-slash', ios: 'eye.slash', android: 'visibility_off' },
|
|
14
|
+
power: { bootstrap: 'power', ios: 'power', android: 'power_settings_new' },
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Model names a host app may look up, each naming the concept it takes its icons from
|
|
3
|
+
ALIASES = {
|
|
4
|
+
address: :pin, agent: :badge, alert: :warning, announcement: :megaphone,
|
|
5
|
+
answer: :question, api: :terminal, app: :window, appointment: :calendar_check,
|
|
6
|
+
area: :map, assessment: :clipboard, attachment: :paperclip, audio: :waveform,
|
|
7
|
+
avatar: :account, backup: :cloud, banner: :image, bill: :invoice,
|
|
8
|
+
booking: :calendar_check, brand: :building, business: :building, call: :phone,
|
|
9
|
+
campaign: :megaphone, category: :tag, charge: :credit_card, city: :building,
|
|
10
|
+
client: :person, comment: :chat, company: :building, configuration: :gear,
|
|
11
|
+
contract: :signature, conversation: :chat, cost: :cash, country: :globe,
|
|
12
|
+
county: :map, coupon: :gift, credential: :key, customer: :person,
|
|
13
|
+
dashboard: :speedometer, deadline: :alarm, delivery: :truck, destination: :pin,
|
|
14
|
+
discount: :percent, echo: :waveform, email: :envelope, employee: :badge,
|
|
15
|
+
episode: :playlist, evaluation: :speedometer, event: :calendar, export: :download,
|
|
16
|
+
feedback: :smiley, file: :document, fleet: :truck, form: :document,
|
|
17
|
+
franchise: :shop, goal: :trophy, group: :people, history: :clock,
|
|
18
|
+
home: :house, import: :upload, integration: :puzzle, inventory: :box,
|
|
19
|
+
issue: :warning, item: :box, job: :hammer, label: :tag,
|
|
20
|
+
lead: :person, location: :pin, log: :list, market: :map,
|
|
21
|
+
member: :person, memo: :note, message: :chat, milestone: :flag,
|
|
22
|
+
movie: :video, notification: :bell, offer: :gift, office: :building,
|
|
23
|
+
optimization: :sliders,
|
|
24
|
+
order: :cart, organization: :building, package: :box, page: :document,
|
|
25
|
+
password: :lock, payment: :credit_card, permission: :key, photo: :image,
|
|
26
|
+
picture: :image, plan: :calendar, platform: :puzzle, plugin: :puzzle,
|
|
27
|
+
preference: :sliders, price: :tag, product: :box, profile: :account,
|
|
28
|
+
project: :folder, prompt: :terminal, provider: :briefcase, purchase: :cart,
|
|
29
|
+
rating: :star, recording: :microphone, region: :map, reminder: :bell,
|
|
30
|
+
review: :star, ride: :car, role: :badge, route: :signpost,
|
|
31
|
+
schedule: :calendar, screen: :display, session: :clock, setting: :gear,
|
|
32
|
+
shift: :clock, shipment: :truck, site: :globe, source: :signpost,
|
|
33
|
+
specialty: :award, staff: :people, state: :map, subscription: :repeat,
|
|
34
|
+
summary: :document, supplier: :truck, survey: :clipboard, sync: :refresh,
|
|
35
|
+
team: :people, territory: :map, todo: :task, token: :key,
|
|
36
|
+
track: :music, transaction: :credit_card, trip: :map, user: :person,
|
|
37
|
+
vehicle: :car, vendor: :shop, vertical: :bar_chart, view: :eye,
|
|
38
|
+
zip: :pin,
|
|
39
|
+
}.freeze
|
|
40
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Numbers drawn rather than listed
|
|
3
|
+
CHARTS = {
|
|
4
|
+
bar_chart: { bootstrap: 'bar-chart', ios: 'chart.bar', android: 'bar_chart' },
|
|
5
|
+
pie_chart: { bootstrap: 'pie-chart', ios: 'chart.pie', android: 'pie_chart' },
|
|
6
|
+
trending_up: {
|
|
7
|
+
bootstrap: 'graph-up', ios: 'chart.line.uptrend.xyaxis', android: 'trending_up',
|
|
8
|
+
},
|
|
9
|
+
speedometer: { bootstrap: 'speedometer', ios: 'gauge', android: 'speed' },
|
|
10
|
+
}.freeze
|
|
11
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Money asked for, money paid, and the goods behind it
|
|
3
|
+
COMMERCE = {
|
|
4
|
+
cart: { bootstrap: 'cart', ios: 'cart', android: 'shopping_cart' },
|
|
5
|
+
bag: { bootstrap: 'bag', ios: 'bag', android: 'shopping_bag' },
|
|
6
|
+
tag: { bootstrap: 'tag', ios: 'tag', android: 'sell' },
|
|
7
|
+
credit_card: { bootstrap: 'credit-card', ios: 'creditcard', android: 'credit_card' },
|
|
8
|
+
cash: { bootstrap: 'cash', ios: 'banknote', android: 'payments' },
|
|
9
|
+
wallet: { bootstrap: 'wallet', ios: 'wallet.pass', android: 'wallet' },
|
|
10
|
+
gift: { bootstrap: 'gift', ios: 'gift', android: 'redeem' },
|
|
11
|
+
percent: { bootstrap: 'percent', ios: 'percent', android: 'percent' },
|
|
12
|
+
ticket: { bootstrap: 'ticket', ios: 'ticket', android: 'confirmation_number' },
|
|
13
|
+
barcode: { bootstrap: 'upc', ios: 'barcode', android: 'barcode' },
|
|
14
|
+
qr_code: { bootstrap: 'qr-code', ios: 'qrcode', android: 'qr_code' },
|
|
15
|
+
receipt: { bootstrap: 'receipt', ios: 'receipt', android: 'receipt' },
|
|
16
|
+
invoice: { bootstrap: 'file-earmark-text', ios: 'doc.text', android: 'receipt_long' },
|
|
17
|
+
}.freeze
|
|
18
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Reaching somebody, and hearing back from them
|
|
3
|
+
COMMUNICATION = {
|
|
4
|
+
phone: { bootstrap: 'telephone', ios: 'phone', android: 'call' },
|
|
5
|
+
mobile: { bootstrap: 'phone', ios: 'smartphone', android: 'mobile' },
|
|
6
|
+
envelope: { bootstrap: 'envelope', ios: 'envelope', android: 'mail' },
|
|
7
|
+
chat: { bootstrap: 'chat', ios: 'message', android: 'chat' },
|
|
8
|
+
bell: { bootstrap: 'bell', ios: 'bell', android: 'notifications' },
|
|
9
|
+
megaphone: { bootstrap: 'megaphone', ios: 'megaphone', android: 'campaign' },
|
|
10
|
+
inbox: { bootstrap: 'inbox', ios: 'tray', android: 'inbox' },
|
|
11
|
+
reply: { bootstrap: 'reply', ios: 'arrowshape.turn.up.left', android: 'reply' },
|
|
12
|
+
}.freeze
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# The hardware an app runs on and talks to
|
|
3
|
+
DEVICES = {
|
|
4
|
+
laptop: { bootstrap: 'laptop', ios: 'laptopcomputer', android: 'computer' },
|
|
5
|
+
keyboard: { bootstrap: 'keyboard', ios: 'keyboard', android: 'keyboard' },
|
|
6
|
+
display: { bootstrap: 'display', ios: 'display', android: 'monitor' },
|
|
7
|
+
server: { bootstrap: 'hdd-rack', ios: 'server.rack', android: 'dns' },
|
|
8
|
+
cloud: { bootstrap: 'cloud', ios: 'cloud', android: 'cloud' },
|
|
9
|
+
wifi: { bootstrap: 'wifi', ios: 'wifi', android: 'wifi' },
|
|
10
|
+
battery: { bootstrap: 'battery', ios: 'battery.100', android: 'battery_full' },
|
|
11
|
+
terminal: { bootstrap: 'terminal', ios: 'terminal', android: 'terminal' },
|
|
12
|
+
}.freeze
|
|
13
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Paperwork, and the boxes it is filed in
|
|
3
|
+
FILES = {
|
|
4
|
+
document: { bootstrap: 'file-earmark', ios: 'doc', android: 'description' },
|
|
5
|
+
folder: { bootstrap: 'folder', ios: 'folder', android: 'folder' },
|
|
6
|
+
archive: { bootstrap: 'archive', ios: 'archivebox', android: 'archive' },
|
|
7
|
+
clipboard: { bootstrap: 'clipboard', ios: 'list.clipboard', android: 'content_paste' },
|
|
8
|
+
book: { bootstrap: 'book', ios: 'book', android: 'book' },
|
|
9
|
+
note: { bootstrap: 'sticky', ios: 'note.text', android: 'sticky_note_2' },
|
|
10
|
+
paperclip: { bootstrap: 'paperclip', ios: 'paperclip', android: 'attach_file' },
|
|
11
|
+
signature: { bootstrap: 'pen', ios: 'signature', android: 'draw' },
|
|
12
|
+
box: { bootstrap: 'box', ios: 'shippingbox', android: 'inventory_2' },
|
|
13
|
+
link: { bootstrap: 'link', ios: 'link', android: 'link' },
|
|
14
|
+
}.freeze
|
|
15
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# How a page shows a set of records, and how a reader narrows it
|
|
3
|
+
LAYOUT = {
|
|
4
|
+
table: { bootstrap: 'table', ios: 'tablecells', android: 'table_chart' },
|
|
5
|
+
list: { bootstrap: 'list-ul', ios: 'list.bullet', android: 'list' },
|
|
6
|
+
grid: { bootstrap: 'grid', ios: 'square.grid.2x2', android: 'grid_view' },
|
|
7
|
+
window: { bootstrap: 'window', ios: 'macwindow', android: 'web_asset' },
|
|
8
|
+
search: { bootstrap: 'search', ios: 'magnifyingglass', android: 'search' },
|
|
9
|
+
filter: { bootstrap: 'funnel', ios: 'line.3.horizontal.decrease', android: 'filter_alt' },
|
|
10
|
+
}.freeze
|
|
11
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Pictures and sound, and the tools that make them
|
|
3
|
+
MEDIA = {
|
|
4
|
+
camera: { bootstrap: 'camera', ios: 'camera', android: 'photo_camera' },
|
|
5
|
+
image: { bootstrap: 'image', ios: 'photo', android: 'image' },
|
|
6
|
+
video: { bootstrap: 'camera-video', ios: 'video', android: 'videocam' },
|
|
7
|
+
microphone: { bootstrap: 'mic', ios: 'mic', android: 'mic' },
|
|
8
|
+
playlist: { bootstrap: 'collection-play', ios: 'play.square.stack', android: 'video_library' },
|
|
9
|
+
waveform: { bootstrap: 'soundwave', ios: 'waveform', android: 'graphic_eq' },
|
|
10
|
+
music: { bootstrap: 'music-note', ios: 'music.note', android: 'music_note' },
|
|
11
|
+
volume: { bootstrap: 'volume-up', ios: 'speaker.wave.2', android: 'volume_up' },
|
|
12
|
+
headphones: { bootstrap: 'headphones', ios: 'headphones', android: 'headphones' },
|
|
13
|
+
palette: { bootstrap: 'palette', ios: 'paintpalette', android: 'palette' },
|
|
14
|
+
brush: { bootstrap: 'brush', ios: 'paintbrush', android: 'brush' },
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Weather, daylight and the world outside the window
|
|
3
|
+
NATURE = {
|
|
4
|
+
sun: { bootstrap: 'sun', ios: 'sun.max', android: 'light_mode' },
|
|
5
|
+
moon: { bootstrap: 'moon', ios: 'moon', android: 'dark_mode' },
|
|
6
|
+
rain: { bootstrap: 'cloud-rain', ios: 'cloud.rain', android: 'rainy' },
|
|
7
|
+
snow: { bootstrap: 'snow', ios: 'snowflake', android: 'weather_snowy' },
|
|
8
|
+
fire: { bootstrap: 'fire', ios: 'flame', android: 'local_fire_department' },
|
|
9
|
+
droplet: { bootstrap: 'droplet', ios: 'drop', android: 'water_drop' },
|
|
10
|
+
bolt: { bootstrap: 'lightning', ios: 'bolt', android: 'bolt' },
|
|
11
|
+
tree: { bootstrap: 'tree', ios: 'tree', android: 'park' },
|
|
12
|
+
}.freeze
|
|
13
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# The people an app keeps records about, and the records themselves
|
|
3
|
+
PEOPLE = {
|
|
4
|
+
person: { bootstrap: 'person', ios: 'person', android: 'person' },
|
|
5
|
+
people: { bootstrap: 'people', ios: 'person.2', android: 'group' },
|
|
6
|
+
account: { bootstrap: 'person-circle', ios: 'person.crop.circle', android: 'account_circle' },
|
|
7
|
+
badge: { bootstrap: 'person-vcard', ios: 'person.text.rectangle', android: 'badge' },
|
|
8
|
+
contact: { bootstrap: 'person-rolodex', ios: 'person.crop.circle', android: 'contacts' },
|
|
9
|
+
}.freeze
|
|
10
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Where something is, from one address to the whole world
|
|
3
|
+
PLACES = {
|
|
4
|
+
house: { bootstrap: 'house', ios: 'house', android: 'home' },
|
|
5
|
+
building: { bootstrap: 'building', ios: 'building', android: 'apartment' },
|
|
6
|
+
shop: { bootstrap: 'shop', ios: 'storefront', android: 'storefront' },
|
|
7
|
+
bank: { bootstrap: 'bank', ios: 'building.columns', android: 'account_balance' },
|
|
8
|
+
map: { bootstrap: 'map', ios: 'map', android: 'map' },
|
|
9
|
+
pin: { bootstrap: 'geo-alt', ios: 'mappin', android: 'location_on' },
|
|
10
|
+
globe: { bootstrap: 'globe', ios: 'globe', android: 'public' },
|
|
11
|
+
flag: { bootstrap: 'flag', ios: 'flag', android: 'flag' },
|
|
12
|
+
signpost: { bootstrap: 'signpost-split', ios: 'signpost.right', android: 'signpost' },
|
|
13
|
+
hospital: { bootstrap: 'hospital', ios: 'cross.case', android: 'local_hospital' },
|
|
14
|
+
compass: { bootstrap: 'compass', ios: 'location.north.circle', android: 'explore' },
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# Who may see a record, and how they prove it
|
|
3
|
+
SECURITY = {
|
|
4
|
+
lock: { bootstrap: 'lock', ios: 'lock', android: 'lock' },
|
|
5
|
+
key: { bootstrap: 'key', ios: 'key', android: 'key' },
|
|
6
|
+
shield: { bootstrap: 'shield', ios: 'shield', android: 'shield' },
|
|
7
|
+
fingerprint: { bootstrap: 'fingerprint', ios: 'touchid', android: 'fingerprint' },
|
|
8
|
+
eye: { bootstrap: 'eye', ios: 'eye', android: 'visibility' },
|
|
9
|
+
}.freeze
|
|
10
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# How a record stands, and what the reader should make of it
|
|
3
|
+
STATUS = {
|
|
4
|
+
warning: {
|
|
5
|
+
bootstrap: 'exclamation-triangle', ios: 'exclamationmark.triangle', android: 'warning',
|
|
6
|
+
},
|
|
7
|
+
info: { bootstrap: 'info-circle', ios: 'info.circle', android: 'info' },
|
|
8
|
+
question: { bootstrap: 'question-circle', ios: 'questionmark.circle', android: 'help' },
|
|
9
|
+
check_circle: { bootstrap: 'check-circle', ios: 'checkmark.circle', android: 'check_circle' },
|
|
10
|
+
close_circle: { bootstrap: 'x-circle', ios: 'xmark.circle', android: 'cancel' },
|
|
11
|
+
circle: { bootstrap: 'circle', ios: 'circle', android: 'circle' },
|
|
12
|
+
square: { bootstrap: 'square', ios: 'square', android: 'square' },
|
|
13
|
+
star: { bootstrap: 'star', ios: 'star', android: 'star' },
|
|
14
|
+
heart: { bootstrap: 'heart', ios: 'heart', android: 'favorite' },
|
|
15
|
+
bookmark: { bootstrap: 'bookmark', ios: 'bookmark', android: 'bookmark' },
|
|
16
|
+
thumbs_up: { bootstrap: 'hand-thumbs-up', ios: 'hand.thumbsup', android: 'thumb_up' },
|
|
17
|
+
thumbs_down: { bootstrap: 'hand-thumbs-down', ios: 'hand.thumbsdown', android: 'thumb_down' },
|
|
18
|
+
trophy: { bootstrap: 'trophy', ios: 'trophy', android: 'trophy' },
|
|
19
|
+
lightbulb: { bootstrap: 'lightbulb', ios: 'lightbulb', android: 'lightbulb' },
|
|
20
|
+
smiley: { bootstrap: 'emoji-smile', ios: 'face.smiling', android: 'sentiment_satisfied' },
|
|
21
|
+
award: { bootstrap: 'award', ios: 'rosette', android: 'workspace_premium' },
|
|
22
|
+
infinity: { bootstrap: 'infinity', ios: 'infinity', android: 'all_inclusive' },
|
|
23
|
+
}.freeze
|
|
24
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# A moment, a duration, and the calendar around them
|
|
3
|
+
TIME = {
|
|
4
|
+
calendar: { bootstrap: 'calendar', ios: 'calendar', android: 'calendar_month' },
|
|
5
|
+
calendar_check: {
|
|
6
|
+
bootstrap: 'calendar-check', ios: 'calendar.badge.checkmark', android: 'event_available',
|
|
7
|
+
},
|
|
8
|
+
clock: { bootstrap: 'clock', ios: 'clock', android: 'schedule' },
|
|
9
|
+
alarm: { bootstrap: 'alarm', ios: 'alarm', android: 'alarm' },
|
|
10
|
+
hourglass: { bootstrap: 'hourglass', ios: 'hourglass', android: 'hourglass_empty' },
|
|
11
|
+
stopwatch: { bootstrap: 'stopwatch', ios: 'stopwatch', android: 'timer' },
|
|
12
|
+
}.freeze
|
|
13
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# The trade a job belongs to, and the settings behind an app
|
|
3
|
+
TOOLS = {
|
|
4
|
+
gear: { bootstrap: 'gear', ios: 'gearshape', android: 'settings' },
|
|
5
|
+
sliders: { bootstrap: 'sliders', ios: 'slider.horizontal.3', android: 'tune' },
|
|
6
|
+
toolbox: { bootstrap: 'tools', ios: 'wrench.and.screwdriver', android: 'handyman' },
|
|
7
|
+
hammer: { bootstrap: 'hammer', ios: 'hammer', android: 'hardware' },
|
|
8
|
+
wrench: { bootstrap: 'wrench', ios: 'wrench', android: 'build' },
|
|
9
|
+
ruler: { bootstrap: 'rulers', ios: 'ruler', android: 'straighten' },
|
|
10
|
+
puzzle: { bootstrap: 'puzzle', ios: 'puzzlepiece', android: 'extension' },
|
|
11
|
+
briefcase: { bootstrap: 'briefcase', ios: 'briefcase', android: 'work' },
|
|
12
|
+
task: { bootstrap: 'check2-square', ios: 'checklist', android: 'task_alt' },
|
|
13
|
+
}.freeze
|
|
14
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Unicon
|
|
2
|
+
# How a person or a parcel gets there
|
|
3
|
+
TRANSPORT = {
|
|
4
|
+
car: { bootstrap: 'car-front', ios: 'car', android: 'directions_car' },
|
|
5
|
+
truck: { bootstrap: 'truck', ios: 'truck.box', android: 'local_shipping' },
|
|
6
|
+
bicycle: { bootstrap: 'bicycle', ios: 'bicycle', android: 'directions_bike' },
|
|
7
|
+
airplane: { bootstrap: 'airplane', ios: 'airplane', android: 'flight' },
|
|
8
|
+
train: { bootstrap: 'train-front', ios: 'train.side.front.car', android: 'train' },
|
|
9
|
+
bus: { bootstrap: 'bus-front', ios: 'bus', android: 'directions_bus' },
|
|
10
|
+
}.freeze
|
|
11
|
+
end
|
data/lib/unicon.rb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'unicon/version'
|
|
2
|
+
require 'unicon/icons/actions/editing'
|
|
3
|
+
require 'unicon/icons/actions/playing'
|
|
4
|
+
require 'unicon/icons/actions/pointing'
|
|
5
|
+
require 'unicon/icons/actions/sending'
|
|
6
|
+
require 'unicon/icons/actions/switching'
|
|
7
|
+
require 'unicon/icons/aliases'
|
|
8
|
+
require 'unicon/icons/charts'
|
|
9
|
+
require 'unicon/icons/commerce'
|
|
10
|
+
require 'unicon/icons/communication'
|
|
11
|
+
require 'unicon/icons/devices'
|
|
12
|
+
require 'unicon/icons/files'
|
|
13
|
+
require 'unicon/icons/layout'
|
|
14
|
+
require 'unicon/icons/media'
|
|
15
|
+
require 'unicon/icons/nature'
|
|
16
|
+
require 'unicon/icons/people'
|
|
17
|
+
require 'unicon/icons/places'
|
|
18
|
+
require 'unicon/icons/security'
|
|
19
|
+
require 'unicon/icons/status'
|
|
20
|
+
require 'unicon/icons/time'
|
|
21
|
+
require 'unicon/icons/tools'
|
|
22
|
+
require 'unicon/icons/transport'
|
|
23
|
+
|
|
24
|
+
# Names one concept's icon in three design systems, so an app states the concept and
|
|
25
|
+
# each client draws it from the set it already ships: `:bootstrap` for Bootstrap Icons,
|
|
26
|
+
# `:ios` for SF Symbols, `:android` for Material Symbols.
|
|
27
|
+
module Unicon
|
|
28
|
+
# One entry per distinct picture, keyed by the thing it draws
|
|
29
|
+
ICONS = {
|
|
30
|
+
**CHARTS, **COMMERCE, **COMMUNICATION, **DEVICES, **FILES, **LAYOUT, **MEDIA, **NATURE,
|
|
31
|
+
**PEOPLE, **PLACES, **SECURITY, **STATUS, **TIME, **TOOLS, **TRANSPORT,
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
# The pictures for what an app does rather than has, which no record is ever named after
|
|
35
|
+
ACTIONS = { **EDITING, **PLAYING, **POINTING, **SENDING, **SWITCHING }.freeze
|
|
36
|
+
|
|
37
|
+
# Everything a lookup resolves, keyed to its picture: the icons, the actions, and the
|
|
38
|
+
# model synonyms borrowing one
|
|
39
|
+
TABLE = {
|
|
40
|
+
**ICONS, **ACTIONS,
|
|
41
|
+
**ALIASES.transform_values { |concept| ICONS[concept] || ACTIONS.fetch(concept) },
|
|
42
|
+
}.each_value { |names| names.each_value(&:freeze).freeze }.freeze
|
|
43
|
+
|
|
44
|
+
# What to show in a picker: one name per picture, so no two entries draw the same thing
|
|
45
|
+
ICON_NAMES = ICONS.keys.sort.freeze
|
|
46
|
+
|
|
47
|
+
# Every key of `TABLE`, alphabetically: icons, actions and model synonyms together
|
|
48
|
+
NAMES = TABLE.keys.sort.freeze
|
|
49
|
+
|
|
50
|
+
# The doing words among them, which `ICON_NAMES` leaves out on purpose
|
|
51
|
+
ACTION_NAMES = ACTIONS.keys.sort.freeze
|
|
52
|
+
|
|
53
|
+
# The three names for a concept, or the circle's when nothing is known by that name
|
|
54
|
+
def self.fetch(concept)
|
|
55
|
+
TABLE.fetch concept.to_s.to_sym, TABLE.fetch(:circle)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Shorthand for `fetch`
|
|
59
|
+
def self.[](concept) = fetch concept
|
|
60
|
+
|
|
61
|
+
# One name per distinct picture: the list to offer somebody choosing an icon
|
|
62
|
+
def self.icons = ICON_NAMES
|
|
63
|
+
|
|
64
|
+
# The union of all three groups — icons, actions and model synonyms — and so every name
|
|
65
|
+
# `fetch` resolves. Ask for this to find out whether a name draws something; ask for
|
|
66
|
+
# `icons` when somebody is choosing one.
|
|
67
|
+
def self.names = NAMES
|
|
68
|
+
|
|
69
|
+
# The pictures for what an app does: fetchable by name, never offered as a choice
|
|
70
|
+
def self.actions = ACTION_NAMES
|
|
71
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: unicon
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- claudiob
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Unicon maps a concept — a house, a phone, a contact — to the name of
|
|
13
|
+
the icon that stands for it in Bootstrap Icons, SF Symbols and Material Symbols.
|
|
14
|
+
A concept is only listed when all three have an icon for it, and every name is checked
|
|
15
|
+
against the list its own design system publishes, so nothing renders as an empty
|
|
16
|
+
box.
|
|
17
|
+
email:
|
|
18
|
+
- claudiob@users.noreply.github.com
|
|
19
|
+
executables: []
|
|
20
|
+
extensions: []
|
|
21
|
+
extra_rdoc_files: []
|
|
22
|
+
files:
|
|
23
|
+
- CHANGELOG.md
|
|
24
|
+
- LICENSE.txt
|
|
25
|
+
- README.md
|
|
26
|
+
- lib/unicon.rb
|
|
27
|
+
- lib/unicon/icons/actions/editing.rb
|
|
28
|
+
- lib/unicon/icons/actions/playing.rb
|
|
29
|
+
- lib/unicon/icons/actions/pointing.rb
|
|
30
|
+
- lib/unicon/icons/actions/sending.rb
|
|
31
|
+
- lib/unicon/icons/actions/switching.rb
|
|
32
|
+
- lib/unicon/icons/aliases.rb
|
|
33
|
+
- lib/unicon/icons/charts.rb
|
|
34
|
+
- lib/unicon/icons/commerce.rb
|
|
35
|
+
- lib/unicon/icons/communication.rb
|
|
36
|
+
- lib/unicon/icons/devices.rb
|
|
37
|
+
- lib/unicon/icons/files.rb
|
|
38
|
+
- lib/unicon/icons/layout.rb
|
|
39
|
+
- lib/unicon/icons/media.rb
|
|
40
|
+
- lib/unicon/icons/nature.rb
|
|
41
|
+
- lib/unicon/icons/people.rb
|
|
42
|
+
- lib/unicon/icons/places.rb
|
|
43
|
+
- lib/unicon/icons/security.rb
|
|
44
|
+
- lib/unicon/icons/status.rb
|
|
45
|
+
- lib/unicon/icons/time.rb
|
|
46
|
+
- lib/unicon/icons/tools.rb
|
|
47
|
+
- lib/unicon/icons/transport.rb
|
|
48
|
+
- lib/unicon/version.rb
|
|
49
|
+
homepage: https://github.com/claudiob/unicon
|
|
50
|
+
licenses:
|
|
51
|
+
- MIT
|
|
52
|
+
metadata:
|
|
53
|
+
source_code_uri: https://github.com/claudiob/unicon
|
|
54
|
+
changelog_uri: https://github.com/claudiob/unicon/blob/main/CHANGELOG.md
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: 3.2.0
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubygems_version: 4.0.3
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: One concept, the icon for it in Bootstrap Icons, SF Symbols and Material.
|
|
72
|
+
test_files: []
|