summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/icon.js
blob: c80258ca74d5152a96520e6727ddd4639301379a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { html } from './vendor/htm-preact.js';

// ── Icons ────────────────────────────────────────────────────────────────────
//
// One stroked set, drawn in currentColor and sized in em, so an icon takes the
// weight and colour of the text beside it. The Administration entry was already
// an outline shield while the rest of the site was colour emoji — a different
// drawing on every operating system, and never the same line weight twice.
//
// The explorer keeps its emoji on purpose. There the icon says what kind of file
// this is, and the colour is doing real work; a wall of identical grey outlines
// would be a worse file list.

const ICON_PATHS = {
  menu:     ['M4 7h16M4 12h16M4 17h16'],
  // A window with a chevron folding into it. Deliberately no shaft: a
  // vertical stem above a receptacle is the download glyph, and the first
  // attempt here read as one.
  tray:     ['M6 4h12a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3z',
             'M8 10l4 4 4-4'],
  bell:     ['M18 9a6 6 0 1 0-12 0c0 6-2.5 7.5-2.5 7.5h17S18 15 18 9',
             'M10.3 20a2 2 0 0 0 3.4 0'],
  shield:   ['M12 3l7.5 3v5.2c0 4.6-3.1 8.6-7.5 10.3-4.4-1.7-7.5-5.7-7.5-10.3V6z'],
  globe:    ['M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18',
             'M3.4 9.2h17.2M3.4 14.8h17.2',
             'M12 3c-2.6 2.4-4 5.6-4 9s1.4 6.6 4 9c2.6-2.4 4-5.6 4-9s-1.4-6.6-4-9'],
  archive:  ['M3 7.5h18v3H3z', 'M4.5 10.5V19a1.5 1.5 0 0 0 1.5 1.5h12a1.5 1.5 0 0 0 1.5-1.5v-8.5',
             'M10 14h4'],
  play:     ['M8 5.5v13l11-6.5z'],
  eye:      ['M2 12s3.6-6.5 10-6.5S22 12 22 12s-3.6 6.5-10 6.5S2 12 2 12',
             'M12 14.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5'],
  // The same eye under a stroke. Drawn as `eye` plus the slash rather than as
  // a broken lid, so the two states of a reveal button are the same glyph and
  // the toggle reads as one control.
  eyeoff:   ['M2 12s3.6-6.5 10-6.5S22 12 22 12s-3.6 6.5-10 6.5S2 12 2 12',
             'M12 14.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5',
             'M4 4l16 16'],
  trash:    ['M4 7h16', 'M10 11v6M14 11v6',
             'M6 7l1 12.5A1.5 1.5 0 0 0 8.5 21h7a1.5 1.5 0 0 0 1.5-1.5L18 7',
             'M9.5 7V5a1.5 1.5 0 0 1 1.5-1.5h2A1.5 1.5 0 0 1 14.5 5v2'],
  user:     ['M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8',
             'M4.5 20a7.5 7.5 0 0 1 15 0'],
  gear:     ['M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6',
             'M19.2 14.4a1.7 1.7 0 0 0 .3 1.9 2 2 0 1 1-2.8 2.8 1.7 1.7 0 0 0-2.9 1.2 2 2 0 0 1-4 0 1.7 1.7 0 0 0-2.9-1.2 2 2 0 1 1-2.8-2.8 1.7 1.7 0 0 0-1.2-2.9 2 2 0 0 1 0-4 1.7 1.7 0 0 0 1.2-2.9 2 2 0 1 1 2.8-2.8 1.7 1.7 0 0 0 2.9-1.2 2 2 0 0 1 4 0 1.7 1.7 0 0 0 2.9 1.2 2 2 0 1 1 2.8 2.8 1.7 1.7 0 0 0 1.2 2.9 2 2 0 0 1 0 4 1.7 1.7 0 0 0-1.5 1.1z'],
  sun:      ['M12 8.2a3.8 3.8 0 1 0 0 7.6 3.8 3.8 0 0 0 0-7.6',
             'M12 2.5v2M12 19.5v2M2.5 12h2M19.5 12h2M5.2 5.2l1.4 1.4M17.4 17.4l1.4 1.4M18.8 5.2l-1.4 1.4M6.6 17.4l-1.4 1.4'],
  moon:     ['M20.8 13.4A8.6 8.6 0 1 1 10.6 3.2a6.9 6.9 0 0 0 10.2 10.2z'],
  power:    ['M12 3.2v8.4', 'M6.9 6.6a7.6 7.6 0 1 0 10.2 0'],
  lock:     ['M5.5 11h13a1 1 0 0 1 1 1v7.5a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1V12a1 1 0 0 1 1-1z',
             'M8 11V7.4a4 4 0 0 1 8 0V11'],
  envelope: ['M4 5.5h16a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-11a1 1 0 0 1 1-1z',
             'M3.4 6.6L12 13.4l8.6-6.8'],
  door:     ['M13.5 3.5H6a1 1 0 0 0-1 1v15a1 1 0 0 0 1 1h7.5',
             'M10.5 12H21', 'M17.8 8.8L21 12l-3.2 3.2'],
  download: ['M12 3.5v12', 'M7.5 11l4.5 4.5 4.5-4.5', 'M4.5 20h15'],
  upload:   ['M12 20.5v-12', 'M7.5 13l4.5-4.5 4.5 4.5', 'M4.5 4h15'],
  transfer: ['M6.5 3.5v11', 'M3.5 11l3 3.5 3-3.5',
             'M17.5 20.5v-11', 'M14.5 13l3-3.5 3 3.5'],
  search:   ['M11 4.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13', 'M15.8 15.8L20.5 20.5'],
  dots:     ['M12 5.6h.01', 'M12 12h.01', 'M12 18.4h.01'],
  checkbox: ['M5.5 4h13a1.5 1.5 0 0 1 1.5 1.5v13a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 4 18.5v-13A1.5 1.5 0 0 1 5.5 4z'],
  home:     ['M4 11.2L12 4.5l8 6.7', 'M6.2 9.8V19a1 1 0 0 0 1 1h9.6a1 1 0 0 0 1-1V9.8'],
  'folder-plus': ['M3.5 6.6a1 1 0 0 1 1-1h4.2l2 2.4h7.8a1 1 0 0 1 1 1v9.4a1 1 0 0 1-1 1h-14a1 1 0 0 1-1-1z',
                  'M12 11.4v5', 'M9.5 13.9h5'],
  plus:     ['M12 5v14', 'M5 12h14'],
  refresh:  ['M20 11a8 8 0 0 0-15.3-2', 'M4 5v4h4',
             'M4 13a8 8 0 0 0 15.3 2', 'M20 19v-4h-4'],
  clip:     ['M20.5 11.8l-8.4 8.4a5.4 5.4 0 0 1-7.6-7.6l8.8-8.8a3.6 3.6 0 0 1 5.1 5.1l-8.8 8.8a1.8 1.8 0 0 1-2.5-2.5l8.1-8.1'],
  pencil:   ['M4 20h4l10.5-10.5a2.1 2.1 0 0 0-3-3L5 17v3',
             'M14.5 6.5l3 3'],
  check:    ['M4.5 12.5l5 5 10-11'],
  chevron:  ['M6 9.5l6 6 6-6'],
  'chevron-left':  ['M14.5 6l-6 6 6 6'],
  'chevron-right': ['M9.5 6l6 6-6 6'],
  close:    ['M6 6l12 12M18 6L6 18'],
  chat:     ['M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'],
  folder:   ['M3.5 6.6a1 1 0 0 1 1-1h4.2l2 2.4h7.8a1 1 0 0 1 1 1v9.4a1 1 0 0 1-1 1h-14a1 1 0 0 1-1-1z'],
  'bell-off': ['M18 9a6 6 0 0 0-12 0c0 6-2.5 7.5-2.5 7.5h17S18 15 18 9',
               'M10.3 20a2 2 0 0 0 3.4 0',
               'M4 4l16 16'],
  server:   ['M4 6.5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2z',
             'M4 15.5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2z',
             'M8 7.5h.01', 'M8 16.5h.01'],
  cast:     ['M2 16.1A5 5 0 0 1 6.9 21', 'M2 12.05A9 9 0 0 1 12.95 21',
             'M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6',
             'M2 21h.01'],
  video:    ['M3.5 6.5a1.5 1.5 0 0 1 1.5-1.5h14a1.5 1.5 0 0 1 1.5 1.5v11a1.5 1.5 0 0 1-1.5 1.5h-14a1.5 1.5 0 0 1-1.5-1.5z',
             'M10 9.5v5l4.5-2.5z'],
  music:    ['M9 18V5l12-2v13',
             'M4 18a2 2 0 1 0 4 0 2 2 0 1 0 -4 0',
             'M16 16a2 2 0 1 0 4 0 2 2 0 1 0 -4 0'],
  pause:    ['M7 5.5v13', 'M17 5.5v13'],
  'skip-next': ['M5 4l10 8-10 8z', 'M19 5v14'],
  'skip-prev': ['M19 4L9 12l10 8z', 'M5 5v14'],
  shuffle:  ['M16 3h5v5', 'M4 20L21 3', 'M21 16v5h-5', 'M15 15l6 6', 'M4 4l5 5'],
  repeat:   ['M17 1l4 4-4 4', 'M3 11V9a4 4 0 0 1 4-4h14',
             'M7 23l-4-4 4-4', 'M21 13v2a4 4 0 0 1-4 4H3'],
  volume:   ['M11 5L6 9H2v6h4l5 4z', 'M15.54 8.46a5 5 0 0 1 0 7.07',
             'M19.07 4.93a10 10 0 0 1 0 14.14'],
  // The spoken-language chooser, and deliberately not a speaker: the video
  // player mounts `<video controls>`, so the browser's own volume speaker sits
  // in the same window, and `volume` above is what the music player uses for
  // the thing it actually names. Two offset bubbles — the same line, said
  // twice — say "which language", which is what the button does.
  // Nothing crosses anything: these are stroked with no fill, and two
  // overlapping outlines read as a tangle at 20 px.
  'speech-pair': ['M11 2.5H4A1.5 1.5 0 0 0 2.5 4v4A1.5 1.5 0 0 0 4 9.5h1V12l3-2.5h3A1.5 1.5 0 0 0 12.5 8V4A1.5 1.5 0 0 0 11 2.5z',
                  'M13 12.5h7A1.5 1.5 0 0 1 21.5 14v4a1.5 1.5 0 0 1-1.5 1.5h-1V22l-3-2.5h-3A1.5 1.5 0 0 1 11.5 18v-4a1.5 1.5 0 0 1 1.5-1.5z'],
  // A frame with two lines at its foot — the conventional subtitle glyph, and
  // the reason `speech-pair` above is not a frame: the two sit side by side in
  // the player's toolbar and have to be told apart at a glance.
  subtitles: ['M3.5 5.5h17a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1h-17a1 1 0 0 1-1-1v-11a1 1 0 0 1 1-1z',
              'M6 14.5h7', 'M15.5 14.5h2.5'],
  image:    ['M5 3.5h14a1.5 1.5 0 0 1 1.5 1.5v14a1.5 1.5 0 0 1-1.5 1.5H5a1.5 1.5 0 0 1-1.5-1.5V5a1.5 1.5 0 0 1 1.5-1.5z',
             'M7 9.5a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0',
             'M20.5 15l-5-5-9.5 9.5'],
  'zoom-in':  ['M11 4.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13', 'M15.8 15.8L20.5 20.5',
               'M11 8v6', 'M8 11h6'],
  'zoom-out': ['M11 4.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13', 'M15.8 15.8L20.5 20.5',
               'M8 11h6'],
  frame: ['M4 9V5a1 1 0 0 1 1-1h4', 'M15 4h4a1 1 0 0 1 1 1v4',
          'M20 15v4a1 1 0 0 1-1 1h-4', 'M9 20H5a1 1 0 0 1-1-1v-4'],
  // A list with a plus: the playlist verbs, and the one button Music's
  // toolbar can spare for them (docs/playlists.md §10.3).
  playlist: ['M4 7h12', 'M4 12h12', 'M4 17h7', 'M17.5 14.5v6', 'M14.5 17.5h6'],
  // A list with a play head at its foot — "after this one", as distinct from
  // `play` (start now) and `plus` (at the end).
  playnext: ['M4 7h10', 'M4 12h10', 'M4 17h6', 'M15.5 13.5v7l5.5-3.5z'],
};

// The M of the wordmark is a picture; the rest is text. Resolved from this
// module's own URL so the hub's fingerprinted path and the application's
// app:// scheme both come out right without either being named here.
const BRAND_M = new URL('./meshbay-m.png', import.meta.url).href;

function Icon({ name, cls = '' }) {
  const paths = ICON_PATHS[name];
  if (!paths) return null;
  return html`
    <svg class="icon ${cls}" viewBox="0 0 24 24" aria-hidden="true" focusable="false"
         fill="none" stroke="currentColor" stroke-width="1.6"
         stroke-linecap="round" stroke-linejoin="round">
      ${paths.map((d, i) => html`<path key=${i} d=${d} />`)}
    </svg>
  `;
}

export { Icon };