Module: Unicon

Defined in:
lib/unicon.rb,
lib/unicon/version.rb,
lib/unicon/icons/time.rb,
lib/unicon/icons/files.rb,
lib/unicon/icons/media.rb,
lib/unicon/icons/tools.rb,
lib/unicon/icons/charts.rb,
lib/unicon/icons/layout.rb,
lib/unicon/icons/nature.rb,
lib/unicon/icons/people.rb,
lib/unicon/icons/places.rb,
lib/unicon/icons/status.rb,
lib/unicon/icons/aliases.rb,
lib/unicon/icons/devices.rb,
lib/unicon/icons/commerce.rb,
lib/unicon/icons/security.rb,
lib/unicon/icons/transport.rb,
lib/unicon/icons/communication.rb,
lib/unicon/icons/actions/editing.rb,
lib/unicon/icons/actions/playing.rb,
lib/unicon/icons/actions/sending.rb,
lib/unicon/icons/actions/pointing.rb,
lib/unicon/icons/actions/switching.rb

Overview

Names one concept's icon in three design systems, so an app states the concept and each client draws it from the set it already ships: :bootstrap for Bootstrap Icons, :ios for SF Symbols, :android for Material Symbols.

Constant Summary collapse

ICONS =

One entry per distinct picture, keyed by the thing it draws

{
  **CHARTS, **COMMERCE, **COMMUNICATION, **DEVICES, **FILES, **LAYOUT, **MEDIA, **NATURE,
  **PEOPLE, **PLACES, **SECURITY, **STATUS, **TIME, **TOOLS, **TRANSPORT,
}.freeze
ACTIONS =

The pictures for what an app does rather than has, which no record is ever named after

{ **EDITING, **PLAYING, **POINTING, **SENDING, **SWITCHING }.freeze
TABLE =

Everything a lookup resolves, keyed to its picture: the icons, the actions, and the model synonyms borrowing one

{
  **ICONS, **ACTIONS,
  **ALIASES.transform_values { |concept| ICONS[concept] || ACTIONS.fetch(concept) },
}.each_value { |names| names.each_value(&:freeze).freeze }.freeze
ICON_NAMES =

What to show in a picker: one name per picture, so no two entries draw the same thing

ICONS.keys.sort.freeze
NAMES =

Every key of TABLE, alphabetically: icons, actions and model synonyms together

TABLE.keys.sort.freeze
ACTION_NAMES =

The doing words among them, which ICON_NAMES leaves out on purpose

ACTIONS.keys.sort.freeze
VERSION =

Version of the gem, read by the gemspec and by hosts checking compatibility.

'0.1.0'
TIME =

A moment, a duration, and the calendar around them

{
  calendar: { bootstrap: 'calendar', ios: 'calendar', android: 'calendar_month' },
  calendar_check: {
    bootstrap: 'calendar-check', ios: 'calendar.badge.checkmark', android: 'event_available',
  },
  clock: { bootstrap: 'clock', ios: 'clock', android: 'schedule' },
  alarm: { bootstrap: 'alarm', ios: 'alarm', android: 'alarm' },
  hourglass: { bootstrap: 'hourglass', ios: 'hourglass', android: 'hourglass_empty' },
  stopwatch: { bootstrap: 'stopwatch', ios: 'stopwatch', android: 'timer' },
}.freeze
FILES =

Paperwork, and the boxes it is filed in

{
  document: { bootstrap: 'file-earmark', ios: 'doc', android: 'description' },
  folder: { bootstrap: 'folder', ios: 'folder', android: 'folder' },
  archive: { bootstrap: 'archive', ios: 'archivebox', android: 'archive' },
  clipboard: { bootstrap: 'clipboard', ios: 'list.clipboard', android: 'content_paste' },
  book: { bootstrap: 'book', ios: 'book', android: 'book' },
  note: { bootstrap: 'sticky', ios: 'note.text', android: 'sticky_note_2' },
  paperclip: { bootstrap: 'paperclip', ios: 'paperclip', android: 'attach_file' },
  signature: { bootstrap: 'pen', ios: 'signature', android: 'draw' },
  box: { bootstrap: 'box', ios: 'shippingbox', android: 'inventory_2' },
  link: { bootstrap: 'link', ios: 'link', android: 'link' },
}.freeze
MEDIA =

Pictures and sound, and the tools that make them

{
  camera: { bootstrap: 'camera', ios: 'camera', android: 'photo_camera' },
  image: { bootstrap: 'image', ios: 'photo', android: 'image' },
  video: { bootstrap: 'camera-video', ios: 'video', android: 'videocam' },
  microphone: { bootstrap: 'mic', ios: 'mic', android: 'mic' },
  playlist: { bootstrap: 'collection-play', ios: 'play.square.stack', android: 'video_library' },
  waveform: { bootstrap: 'soundwave', ios: 'waveform', android: 'graphic_eq' },
  music: { bootstrap: 'music-note', ios: 'music.note', android: 'music_note' },
  volume: { bootstrap: 'volume-up', ios: 'speaker.wave.2', android: 'volume_up' },
  headphones: { bootstrap: 'headphones', ios: 'headphones', android: 'headphones' },
  palette: { bootstrap: 'palette', ios: 'paintpalette', android: 'palette' },
  brush: { bootstrap: 'brush', ios: 'paintbrush', android: 'brush' },
}.freeze
TOOLS =

The trade a job belongs to, and the settings behind an app

{
  gear: { bootstrap: 'gear', ios: 'gearshape', android: 'settings' },
  sliders: { bootstrap: 'sliders', ios: 'slider.horizontal.3', android: 'tune' },
  toolbox: { bootstrap: 'tools', ios: 'wrench.and.screwdriver', android: 'handyman' },
  hammer: { bootstrap: 'hammer', ios: 'hammer', android: 'hardware' },
  wrench: { bootstrap: 'wrench', ios: 'wrench', android: 'build' },
  ruler: { bootstrap: 'rulers', ios: 'ruler', android: 'straighten' },
  puzzle: { bootstrap: 'puzzle', ios: 'puzzlepiece', android: 'extension' },
  briefcase: { bootstrap: 'briefcase', ios: 'briefcase', android: 'work' },
  task: { bootstrap: 'check2-square', ios: 'checklist', android: 'task_alt' },
}.freeze
CHARTS =

Numbers drawn rather than listed

{
  bar_chart: { bootstrap: 'bar-chart', ios: 'chart.bar', android: 'bar_chart' },
  pie_chart: { bootstrap: 'pie-chart', ios: 'chart.pie', android: 'pie_chart' },
  trending_up: {
    bootstrap: 'graph-up', ios: 'chart.line.uptrend.xyaxis', android: 'trending_up',
  },
  speedometer: { bootstrap: 'speedometer', ios: 'gauge', android: 'speed' },
}.freeze
LAYOUT =

How a page shows a set of records, and how a reader narrows it

{
  table: { bootstrap: 'table', ios: 'tablecells', android: 'table_chart' },
  list: { bootstrap: 'list-ul', ios: 'list.bullet', android: 'list' },
  grid: { bootstrap: 'grid', ios: 'square.grid.2x2', android: 'grid_view' },
  window: { bootstrap: 'window', ios: 'macwindow', android: 'web_asset' },
  search: { bootstrap: 'search', ios: 'magnifyingglass', android: 'search' },
  filter: { bootstrap: 'funnel', ios: 'line.3.horizontal.decrease', android: 'filter_alt' },
}.freeze
NATURE =

Weather, daylight and the world outside the window

{
  sun: { bootstrap: 'sun', ios: 'sun.max', android: 'light_mode' },
  moon: { bootstrap: 'moon', ios: 'moon', android: 'dark_mode' },
  rain: { bootstrap: 'cloud-rain', ios: 'cloud.rain', android: 'rainy' },
  snow: { bootstrap: 'snow', ios: 'snowflake', android: 'weather_snowy' },
  fire: { bootstrap: 'fire', ios: 'flame', android: 'local_fire_department' },
  droplet: { bootstrap: 'droplet', ios: 'drop', android: 'water_drop' },
  bolt: { bootstrap: 'lightning', ios: 'bolt', android: 'bolt' },
  tree: { bootstrap: 'tree', ios: 'tree', android: 'park' },
}.freeze
PEOPLE =

The people an app keeps records about, and the records themselves

{
  person: { bootstrap: 'person', ios: 'person', android: 'person' },
  people: { bootstrap: 'people', ios: 'person.2', android: 'group' },
  account: { bootstrap: 'person-circle', ios: 'person.crop.circle', android: 'account_circle' },
  badge: { bootstrap: 'person-vcard', ios: 'person.text.rectangle', android: 'badge' },
  contact: { bootstrap: 'person-rolodex', ios: 'person.crop.circle', android: 'contacts' },
}.freeze
PLACES =

Where something is, from one address to the whole world

{
  house: { bootstrap: 'house', ios: 'house', android: 'home' },
  building: { bootstrap: 'building', ios: 'building', android: 'apartment' },
  shop: { bootstrap: 'shop', ios: 'storefront', android: 'storefront' },
  bank: { bootstrap: 'bank', ios: 'building.columns', android: 'account_balance' },
  map: { bootstrap: 'map', ios: 'map', android: 'map' },
  pin: { bootstrap: 'geo-alt', ios: 'mappin', android: 'location_on' },
  globe: { bootstrap: 'globe', ios: 'globe', android: 'public' },
  flag: { bootstrap: 'flag', ios: 'flag', android: 'flag' },
  signpost: { bootstrap: 'signpost-split', ios: 'signpost.right', android: 'signpost' },
  hospital: { bootstrap: 'hospital', ios: 'cross.case', android: 'local_hospital' },
  compass: { bootstrap: 'compass', ios: 'location.north.circle', android: 'explore' },
}.freeze
STATUS =

How a record stands, and what the reader should make of it

{
  warning: {
    bootstrap: 'exclamation-triangle', ios: 'exclamationmark.triangle', android: 'warning',
  },
  info: { bootstrap: 'info-circle', ios: 'info.circle', android: 'info' },
  question: { bootstrap: 'question-circle', ios: 'questionmark.circle', android: 'help' },
  check_circle: { bootstrap: 'check-circle', ios: 'checkmark.circle', android: 'check_circle' },
  close_circle: { bootstrap: 'x-circle', ios: 'xmark.circle', android: 'cancel' },
  circle: { bootstrap: 'circle', ios: 'circle', android: 'circle' },
  square: { bootstrap: 'square', ios: 'square', android: 'square' },
  star: { bootstrap: 'star', ios: 'star', android: 'star' },
  heart: { bootstrap: 'heart', ios: 'heart', android: 'favorite' },
  bookmark: { bootstrap: 'bookmark', ios: 'bookmark', android: 'bookmark' },
  thumbs_up: { bootstrap: 'hand-thumbs-up', ios: 'hand.thumbsup', android: 'thumb_up' },
  thumbs_down: { bootstrap: 'hand-thumbs-down', ios: 'hand.thumbsdown', android: 'thumb_down' },
  trophy: { bootstrap: 'trophy', ios: 'trophy', android: 'trophy' },
  lightbulb: { bootstrap: 'lightbulb', ios: 'lightbulb', android: 'lightbulb' },
  smiley: { bootstrap: 'emoji-smile', ios: 'face.smiling', android: 'sentiment_satisfied' },
  award: { bootstrap: 'award', ios: 'rosette', android: 'workspace_premium' },
  infinity: { bootstrap: 'infinity', ios: 'infinity', android: 'all_inclusive' },
}.freeze
ALIASES =

Model names a host app may look up, each naming the concept it takes its icons from

{
  address: :pin, agent: :badge, alert: :warning, announcement: :megaphone,
  answer: :question, api: :terminal, app: :window, appointment: :calendar_check,
  area: :map, assessment: :clipboard, attachment: :paperclip, audio: :waveform,
  avatar: :account, backup: :cloud, banner: :image, bill: :invoice,
  booking: :calendar_check, brand: :building, business: :building, call: :phone,
  campaign: :megaphone, category: :tag, charge: :credit_card, city: :building,
  client: :person, comment: :chat, company: :building, configuration: :gear,
  contract: :signature, conversation: :chat, cost: :cash, country: :globe,
  county: :map, coupon: :gift, credential: :key, customer: :person,
  dashboard: :speedometer, deadline: :alarm, delivery: :truck, destination: :pin,
  discount: :percent, echo: :waveform, email: :envelope, employee: :badge,
  episode: :playlist, evaluation: :speedometer, event: :calendar, export: :download,
  feedback: :smiley, file: :document, fleet: :truck, form: :document,
  franchise: :shop, goal: :trophy, group: :people, history: :clock,
  home: :house, import: :upload, integration: :puzzle, inventory: :box,
  issue: :warning, item: :box, job: :hammer, label: :tag,
  lead: :person, location: :pin, log: :list, market: :map,
  member: :person, memo: :note, message: :chat, milestone: :flag,
  movie: :video, notification: :bell, offer: :gift, office: :building,
  optimization: :sliders,
  order: :cart, organization: :building, package: :box, page: :document,
  password: :lock, payment: :credit_card, permission: :key, photo: :image,
  picture: :image, plan: :calendar, platform: :puzzle, plugin: :puzzle,
  preference: :sliders, price: :tag, product: :box, profile: :account,
  project: :folder, prompt: :terminal, provider: :briefcase, purchase: :cart,
  rating: :star, recording: :microphone, region: :map, reminder: :bell,
  review: :star, ride: :car, role: :badge, route: :signpost,
  schedule: :calendar, screen: :display, session: :clock, setting: :gear,
  shift: :clock, shipment: :truck, site: :globe, source: :signpost,
  specialty: :award, staff: :people, state: :map, subscription: :repeat,
  summary: :document, supplier: :truck, survey: :clipboard, sync: :refresh,
  team: :people, territory: :map, todo: :task, token: :key,
  track: :music, transaction: :credit_card, trip: :map, user: :person,
  vehicle: :car, vendor: :shop, vertical: :bar_chart, view: :eye,
  zip: :pin,
}.freeze
DEVICES =

The hardware an app runs on and talks to

{
  laptop: { bootstrap: 'laptop', ios: 'laptopcomputer', android: 'computer' },
  keyboard: { bootstrap: 'keyboard', ios: 'keyboard', android: 'keyboard' },
  display: { bootstrap: 'display', ios: 'display', android: 'monitor' },
  server: { bootstrap: 'hdd-rack', ios: 'server.rack', android: 'dns' },
  cloud: { bootstrap: 'cloud', ios: 'cloud', android: 'cloud' },
  wifi: { bootstrap: 'wifi', ios: 'wifi', android: 'wifi' },
  battery: { bootstrap: 'battery', ios: 'battery.100', android: 'battery_full' },
  terminal: { bootstrap: 'terminal', ios: 'terminal', android: 'terminal' },
}.freeze
COMMERCE =

Money asked for, money paid, and the goods behind it

{
  cart: { bootstrap: 'cart', ios: 'cart', android: 'shopping_cart' },
  bag: { bootstrap: 'bag', ios: 'bag', android: 'shopping_bag' },
  tag: { bootstrap: 'tag', ios: 'tag', android: 'sell' },
  credit_card: { bootstrap: 'credit-card', ios: 'creditcard', android: 'credit_card' },
  cash: { bootstrap: 'cash', ios: 'banknote', android: 'payments' },
  wallet: { bootstrap: 'wallet', ios: 'wallet.pass', android: 'wallet' },
  gift: { bootstrap: 'gift', ios: 'gift', android: 'redeem' },
  percent: { bootstrap: 'percent', ios: 'percent', android: 'percent' },
  ticket: { bootstrap: 'ticket', ios: 'ticket', android: 'confirmation_number' },
  barcode: { bootstrap: 'upc', ios: 'barcode', android: 'barcode' },
  qr_code: { bootstrap: 'qr-code', ios: 'qrcode', android: 'qr_code' },
  receipt: { bootstrap: 'receipt', ios: 'receipt', android: 'receipt' },
  invoice: { bootstrap: 'file-earmark-text', ios: 'doc.text', android: 'receipt_long' },
}.freeze
SECURITY =

Who may see a record, and how they prove it

{
  lock: { bootstrap: 'lock', ios: 'lock', android: 'lock' },
  key: { bootstrap: 'key', ios: 'key', android: 'key' },
  shield: { bootstrap: 'shield', ios: 'shield', android: 'shield' },
  fingerprint: { bootstrap: 'fingerprint', ios: 'touchid', android: 'fingerprint' },
  eye: { bootstrap: 'eye', ios: 'eye', android: 'visibility' },
}.freeze
TRANSPORT =

How a person or a parcel gets there

{
  car: { bootstrap: 'car-front', ios: 'car', android: 'directions_car' },
  truck: { bootstrap: 'truck', ios: 'truck.box', android: 'local_shipping' },
  bicycle: { bootstrap: 'bicycle', ios: 'bicycle', android: 'directions_bike' },
  airplane: { bootstrap: 'airplane', ios: 'airplane', android: 'flight' },
  train: { bootstrap: 'train-front', ios: 'train.side.front.car', android: 'train' },
  bus: { bootstrap: 'bus-front', ios: 'bus', android: 'directions_bus' },
}.freeze
COMMUNICATION =

Reaching somebody, and hearing back from them

{
  phone: { bootstrap: 'telephone', ios: 'phone', android: 'call' },
  mobile: { bootstrap: 'phone', ios: 'smartphone', android: 'mobile' },
  envelope: { bootstrap: 'envelope', ios: 'envelope', android: 'mail' },
  chat: { bootstrap: 'chat', ios: 'message', android: 'chat' },
  bell: { bootstrap: 'bell', ios: 'bell', android: 'notifications' },
  megaphone: { bootstrap: 'megaphone', ios: 'megaphone', android: 'campaign' },
  inbox: { bootstrap: 'inbox', ios: 'tray', android: 'inbox' },
  reply: { bootstrap: 'reply', ios: 'arrowshape.turn.up.left', android: 'reply' },
}.freeze
EDITING =

Changing a record, or what is shown of it

{
  plus: { bootstrap: 'plus', ios: 'plus', android: 'add' },
  minus: { bootstrap: 'dash', ios: 'minus', android: 'remove' },
  check: { bootstrap: 'check', ios: 'checkmark', android: 'check' },
  close: { bootstrap: 'x', ios: 'xmark', android: 'close' },
  pencil: { bootstrap: 'pencil', ios: 'pencil', android: 'edit' },
  trash: { bootstrap: 'trash', ios: 'trash', android: 'delete' },
  copy: { bootstrap: 'copy', ios: 'doc.on.doc', android: 'content_copy' },
  scissors: { bootstrap: 'scissors', ios: 'scissors', android: 'content_cut' },
  undo: { bootstrap: 'arrow-counterclockwise', ios: 'arrow.uturn.backward', android: 'undo' },
  refresh: { bootstrap: 'arrow-clockwise', ios: 'arrow.clockwise', android: 'refresh' },
  sort: { bootstrap: 'sort-down', ios: 'arrow.up.arrow.down', android: 'sort' },
  zoom_in: { bootstrap: 'zoom-in', ios: 'plus.magnifyingglass', android: 'zoom_in' },
  zoom_out: { bootstrap: 'zoom-out', ios: 'minus.magnifyingglass', android: 'zoom_out' },
  person_add: { bootstrap: 'person-plus', ios: 'person.badge.plus', android: 'person_add' },
  person_remove: {
    bootstrap: 'person-dash', ios: 'person.badge.minus', android: 'person_remove',
  },
}.freeze
PLAYING =

Running a recording, and how it repeats

{
  play: { bootstrap: 'play', ios: 'play', android: 'play_arrow' },
  pause: { bootstrap: 'pause', ios: 'pause', android: 'pause' },
  stop: { bootstrap: 'stop', ios: 'stop', android: 'stop' },
  skip_next: { bootstrap: 'skip-forward', ios: 'forward.end', android: 'skip_next' },
  shuffle: { bootstrap: 'shuffle', ios: 'shuffle', android: 'shuffle' },
  repeat: { bootstrap: 'repeat', ios: 'repeat', android: 'repeat' },
}.freeze
SENDING =

Moving something out of the app, or into it

{
  send: { bootstrap: 'send', ios: 'paperplane', android: 'send' },
  share: { bootstrap: 'share', ios: 'square.and.arrow.up', android: 'share' },
  download: { bootstrap: 'download', ios: 'arrow.down.circle', android: 'download' },
  upload: { bootstrap: 'upload', ios: 'arrow.up.circle', android: 'upload' },
  printer: { bootstrap: 'printer', ios: 'printer', android: 'print' },
}.freeze
POINTING =

Where a control leads, and what it opens

{
  chevron_up: { bootstrap: 'chevron-up', ios: 'chevron.up', android: 'keyboard_arrow_up' },
  chevron_down: {
    bootstrap: 'chevron-down', ios: 'chevron.down', android: 'keyboard_arrow_down',
  },
  chevron_left: {
    bootstrap: 'chevron-left', ios: 'chevron.left', android: 'keyboard_arrow_left',
  },
  chevron_right: {
    bootstrap: 'chevron-right', ios: 'chevron.right', android: 'keyboard_arrow_right',
  },
  arrow_up: { bootstrap: 'arrow-up', ios: 'arrow.up', android: 'arrow_upward' },
  arrow_down: { bootstrap: 'arrow-down', ios: 'arrow.down', android: 'arrow_downward' },
  arrow_left: { bootstrap: 'arrow-left', ios: 'arrow.left', android: 'arrow_back' },
  arrow_right: { bootstrap: 'arrow-right', ios: 'arrow.right', android: 'arrow_forward' },
  menu: { bootstrap: 'list', ios: 'line.3.horizontal', android: 'menu' },
  ellipsis: { bootstrap: 'three-dots', ios: 'ellipsis', android: 'more_horiz' },
  sidebar: { bootstrap: 'layout-sidebar', ios: 'sidebar.left', android: 'view_sidebar' },
}.freeze
SWITCHING =

Turning something on, off, or open

{
  login: {
    bootstrap: 'box-arrow-in-right',
    ios: 'rectangle.portrait.and.arrow.forward',
    android: 'login',
  },
  logout: {
    bootstrap: 'box-arrow-right', ios: 'rectangle.portrait.and.arrow.right', android: 'logout',
  },
  unlock: { bootstrap: 'unlock', ios: 'lock.open', android: 'lock_open' },
  eye_slash: { bootstrap: 'eye-slash', ios: 'eye.slash', android: 'visibility_off' },
  power: { bootstrap: 'power', ios: 'power', android: 'power_settings_new' },
}.freeze

Class Method Summary collapse

Class Method Details

.[](concept) ⇒ Object

Shorthand for fetch



59
# File 'lib/unicon.rb', line 59

def self.[](concept) = fetch concept

.actionsObject

The pictures for what an app does: fetchable by name, never offered as a choice



70
# File 'lib/unicon.rb', line 70

def self.actions = ACTION_NAMES

.fetch(concept) ⇒ Object

The three names for a concept, or the circle's when nothing is known by that name



54
55
56
# File 'lib/unicon.rb', line 54

def self.fetch(concept)
  TABLE.fetch concept.to_s.to_sym, TABLE.fetch(:circle)
end

.iconsObject

One name per distinct picture: the list to offer somebody choosing an icon



62
# File 'lib/unicon.rb', line 62

def self.icons = ICON_NAMES

.namesObject

The union of all three groups — icons, actions and model synonyms — and so every name fetch resolves. Ask for this to find out whether a name draws something; ask for icons when somebody is choosing one.



67
# File 'lib/unicon.rb', line 67

def self.names = NAMES