Project

General

Profile

Feature #23980 » 0001-Replace-icon-images-with-inline-SVG-icons.patch

Marius BĂLTEANU, 2021-10-10 20:16

View differences:

app/controllers/application_controller.rb
28 28
  include Redmine::Hook::Helper
29 29
  include RoutesHelper
30 30
  include AvatarsHelper
31
  include IconsHelper
31 32

  
32 33
  helper :routes
33 34
  helper :avatars
35
  helper :icons
34 36

  
35 37
  class_attribute :accept_api_auth_actions
36 38
  class_attribute :accept_rss_auth_actions
app/helpers/application_helper.rb
126 126
  # * :download - Force download (default: false)
127 127
  def link_to_attachment(attachment, options={})
128 128
    text = options.delete(:text) || attachment.filename
129
    icon = 'paperclip'
130
    icon_only = false
131

  
129 132
    if options.delete(:download)
130 133
      route_method = :download_named_attachment_url
131 134
      options[:filename] = attachment.filename
135
      icon = 'download'
136
      icon_only = true
132 137
    else
133 138
      route_method = :attachment_url
134 139
      # make sure we don't have an extraneous :filename in the options
......
137 142
    html_options = options.slice!(:only_path, :filename)
138 143
    options[:only_path] = true unless options.key?(:only_path)
139 144
    url = send(route_method, attachment, options)
140
    link_to text, url, html_options
145
    link_to labeled_icon(text, icon, icon_only: icon_only, size: 12), url, html_options
141 146
  end
142 147

  
143 148
  # Generates a link to a SCM revision
......
797 802
    content = capture(&block)
798 803
    if content.present?
799 804
      trigger =
800
        content_tag('span', l(:button_actions), :class => 'icon-only icon-actions',
805
        content_tag('span', labeled_icon(l(:button_actions), 'ellipsis-h', icon_only: true), :class => 'icon icon-actions icon-svg',
801 806
                    :title => l(:button_actions))
802 807
      trigger = content_tag('span', trigger, :class => 'drdn-trigger')
803 808
      content = content_tag('div', content, :class => 'drdn-items')
......
1832 1837

  
1833 1838
  def copy_object_url_link(url)
1834 1839
    link_to_function(
1835
      l(:button_copy_link), 'copyTextToClipboard(this);',
1836
      class: 'icon icon-copy-link',
1840
      labeled_icon(l(:button_copy_link), 'link'), 'copyTextToClipboard(this);',
1841
      class: 'icon icon-copy-link icon-svg',
1837 1842
      data: {'clipboard-text' => url}
1838 1843
    )
1839 1844
  end
app/helpers/icons_helper.rb
1
# frozen_string_literal: true
2

  
3
module IconsHelper
4
  DEFAULT_ICON_SIZE = 14
5
  DEFAULT_ICON_PATH = 'public/svgs'
6

  
7
  def labeled_icon(label, icon_name = nil, size: DEFAULT_ICON_SIZE, css_class: nil, icon_only: false, path: DEFAULT_ICON_PATH)
8
    css_classes = []
9
    css_classes << "s#{size}" if size
10
    css_classes << "#{css_class}" unless css_class.blank?
11

  
12
    svg = svg_file(icon_name, css_classes, path)
13

  
14
    if icon_only
15
      svg
16
    else
17
      svg + label
18
    end
19
  end
20

  
21
  private
22

  
23
  def svg_file(icon, css_class, path)
24
    file_path = "#{Rails.root}/#{path}/#{icon}.svg"
25

  
26
    if File.exists?(file_path)
27
      # cache { File.read(file_path).html_safe }
28
      file = File.read(file_path)
29

  
30
      doc = Nokogiri::HTML::DocumentFragment.parse file
31
      svg = doc.at_css 'svg'
32
      svg["class"] = css_class.join(' ')
33
    else
34
      doc = "<!-- SVG #{icon} not found -->"
35
    end
36

  
37
    raw doc
38
  end
39
end
app/helpers/journals_helper.rb
34 34
    dropbown_links << copy_object_url_link(issue_url(issue, anchor: "note-#{indice}", only_path: false))
35 35
    if journal.notes.present?
36 36
      if options[:reply_links]
37
        links << link_to(l(:button_quote),
37
        links << link_to(labeled_icon(l(:button_quote), 'quote-left', icon_only: true),
38 38
                         quoted_issue_path(issue, :journal_id => journal, :journal_indice => indice),
39 39
                         :remote => true,
40 40
                         :method => 'post',
41 41
                         :title => l(:button_quote),
42
                         :class => 'icon-only icon-comment'
42
                         :class => 'icon-only icon-comment icon-svg'
43 43
                        )
44 44
      end
45 45
      if journal.editable_by?(User.current)
46
        links << link_to(l(:button_edit),
46
        links << link_to(labeled_icon(l(:button_edit), 'pen-alt', icon_only: true),
47 47
                         edit_journal_path(journal),
48 48
                         :remote => true,
49 49
                         :method => 'get',
50 50
                         :title => l(:button_edit),
51
                         :class => 'icon-only icon-edit'
51
                         :class => 'icon-only icon-edit icon-svg'
52 52
                        )
53
        dropbown_links << link_to(l(:button_delete),
53
        dropbown_links << link_to(labeled_icon(l(:button_delete), 'trash-alt'),
54 54
                                  journal_path(journal, :journal => {:notes => ""}),
55 55
                                  :remote => true,
56 56
                                  :method => 'put', :data => {:confirm => l(:text_are_you_sure)},
57
                                  :class => 'icon icon-del'
57
                                  :class => 'icon icon-del icon-svg'
58 58
                                 )
59 59
      end
60 60
    end
app/helpers/watchers_helper.rb
26 26
    return '' unless objects.any?
27 27

  
28 28
    watched = Watcher.any_watched?(objects, user)
29
    css = [watcher_css(objects), watched ? 'icon icon-fav' : 'icon icon-fav-off'].join(' ')
29
    css = [watcher_css(objects), watched ? 'icon icon-fav' : 'icon icon-fav-off icon-svg'].join(' ')
30 30
    text = watched ? l(:button_unwatch) : l(:button_watch)
31 31
    url = watch_path(
32 32
      :object_type => objects.first.class.to_s.underscore,
33 33
      :object_id => (objects.size == 1 ? objects.first.id : objects.map(&:id).sort)
34 34
    )
35 35
    method = watched ? 'delete' : 'post'
36
    icon = watched ? 'star' : 'star-off'
36 37

  
37
    link_to text, url, :remote => true, :method => method, :class => css
38
    link_to labeled_icon(text, icon), url, :remote => true, :method => method, :class => css
38 39
  end
39 40

  
40 41
  # Returns the css class used to identify watch links for a given +object+
app/views/attachments/_links.html.erb
1 1
<div class="attachments">
2 2
<div class="contextual">
3
  <%= link_to(l(:label_edit_attachments),
3
  <%= link_to(labeled_icon(l(:label_edit_attachments), 'pen-alt', icon_only: true),
4 4
              container_attachments_edit_path(container),
5 5
              :title => l(:label_edit_attachments),
6
              :class => 'icon-only icon-edit'
6
              :class => 'icon-only icon-edit icon-svg'
7 7
             ) if options[:editable] %>
8
  <%= link_to(l(:label_download_all_attachments),
8
  <%= link_to(labeled_icon(l(:label_download_all_attachments), 'download', icon_only: true),
9 9
              container_attachments_download_path(container),
10 10
              :title => l(:label_download_all_attachments),
11
              :class => 'icon-only icon-download'
11
              :class => 'icon-only icon-download icon-svg'
12 12
             ) if attachments.size > 1 %>
13 13
</div>
14 14
<table>
15 15
<% for attachment in attachments %>
16 16
<tr>
17 17
  <td>
18
    <%= link_to_attachment attachment, class: 'icon icon-attachment' -%>
18
    <%= link_to_attachment attachment, class: 'icon icon-attachment icon-svg' -%>
19 19
    <span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
20
    <%= link_to_attachment attachment, class: 'icon-only icon-download', title: l(:button_download), download: true -%>
20
    <%= link_to_attachment attachment, class: 'icon-only icon-download icon-svg', title: l(:button_download), download: true -%>
21 21
  </td>
22 22
  <td><%= attachment.description unless attachment.description.blank? %></td>
23 23
  <td>
......
27 27
  </td>
28 28
  <td>
29 29
    <% if options[:deletable] %>
30
      <%= link_to l(:button_delete), attachment_path(attachment),
30
      <%= link_to labeled_icon(l(:button_delete), 'trash-alt', icon_only: true, size: 12), attachment_path(attachment),
31 31
                  :data => {:confirm => l(:text_are_you_sure)},
32 32
                  :method => :delete,
33
                  :class => 'delete icon-only icon-del',
33
                  :class => 'delete icon-only icon-del icon-svg',
34 34
                  :title => l(:button_delete) %>
35 35
    <% end %>
36 36
  </td>
app/views/issues/_action_menu.html.erb
1 1
<div class="contextual">
2
<%= link_to l(:button_edit), edit_issue_path(@issue),
2
<%= link_to labeled_icon(l(:button_edit), 'pen-alt'), edit_issue_path(@issue),
3 3
            :onclick => 'showAndScrollTo("update", "issue_notes"); return false;',
4
            :class => 'icon icon-edit', :accesskey => accesskey(:edit) if @issue.editable? %>
5
<%= link_to l(:button_log_time), new_issue_time_entry_path(@issue),
6
            :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project) %>
4
            :class => 'icon icon-edit icon-svg', :accesskey => accesskey(:edit) if @issue.editable? %>
5
<%= link_to labeled_icon(l(:button_log_time), 'time-add'), new_issue_time_entry_path(@issue),
6
            :class => 'icon icon-time-add icon-svg' if User.current.allowed_to?(:log_time, @project) %>
7 7
<%= watcher_link(@issue, User.current) %>
8
<%= link_to l(:button_copy), project_copy_issue_path(@project, @issue),
9
            :class => 'icon icon-copy' if User.current.allowed_to?(:copy_issues, @project) && Issue.allowed_target_projects.any? %>
8
<%= link_to labeled_icon(l(:button_copy), 'copy'), project_copy_issue_path(@project, @issue),
9
            :class => 'icon icon-copy icon-svg' if User.current.allowed_to?(:copy_issues, @project) && Issue.allowed_target_projects.any? %>
10 10
<%= actions_dropdown do %>
11 11
  <%= copy_object_url_link(issue_url(@issue, only_path: false)) %>
12
  <%= link_to l(:button_delete), issue_path(@issue),
12
  <%= link_to labeled_icon(l(:button_delete), 'trash-alt'), issue_path(@issue),
13 13
              :data => {:confirm => issues_destroy_confirmation_message(@issue)},
14
              :method => :delete, :class => 'icon icon-del' if @issue.deletable? %>
14
              :method => :delete, :class => 'icon icon-del icon-svg' if @issue.deletable? %>
15 15
<% end %>
16 16
</div>
app/views/issues/index.html.erb
1 1
<div class="contextual">
2 2
  <% if User.current.allowed_to?(:add_issues, @project, :global => true) && (@project.nil? || Issue.allowed_target_trackers(@project).any?) %>
3
    <%= link_to l(:label_issue_new), _new_project_issue_path(@project), :class => 'icon icon-add new-issue' %>
3
    <%= link_to labeled_icon(l(:label_issue_new), 'plus-circle'), _new_project_issue_path(@project), :class => 'icon icon-svg icon-add new-issue' %>
4 4
  <% end %>
5 5
  <%= actions_dropdown do %>
6 6
    <% if @project %>
app/views/issues/show.html.erb
83 83
<hr />
84 84
<div class="description">
85 85
  <div class="contextual">
86
  <%= link_to l(:button_quote), quoted_issue_path(@issue), :remote => true, :method => 'post', :class => 'icon icon-comment' if @issue.notes_addable? %>
86
  <%= link_to labeled_icon(l(:button_quote), 'quote-left'), quoted_issue_path(@issue), :remote => true, :method => 'post', :class => 'icon icon-comment icon-svg' if @issue.notes_addable? %>
87 87
  </div>
88 88

  
89 89
  <p><strong><%=l(:field_description)%></strong></p>
public/stylesheets/application.css
223 223

  
224 224
.contextual .drdn-content {top:18px;}
225 225
.contextual .drdn-items {padding:2px; min-width: 160px;}
226
.contextual .drdn-items>a {padding: 5px 8px;}
227
.contextual .drdn-items>a.icon {padding-left: 24px; background-position-x: 4px;}
226
.contextual .drdn-items>a {display: flex; padding: 5px 8px;}
227
.contextual .drdn-items>a.icon:not(.icon-svg) {padding-left: 24px; background-position-x: 4px;}
228 228
.contextual .drdn-items>a:hover {color:#2A5685; border:1px solid #628db6; background-color:#eef5fd; border-radius:3px;}
229 229

  
230 230
#project-jump.drdn {width:200px;display:inline-block;}
......
464 464
  width: .6em; height: .6em;
465 465
}
466 466
.contextual {float:right; white-space: nowrap; line-height:1.4em;margin:5px 0px; padding-left: 10px; font-size:0.9em;}
467
.contextual .icon {padding-top: 2px; padding-bottom: 3px;}
467
.contextual .icon:not(.icon-svg) {padding-top: 2px; padding-bottom: 3px;}
468 468
.contextual input, .contextual select {font-size:0.9em;}
469 469
.message .contextual { margin-top: 0; }
470 470

  
......
1497 1497
  white-space: pre-wrap;
1498 1498
}
1499 1499

  
1500
/***** SVGs ******/
1501
.s16 {
1502
  width: 16px;
1503
  height: 16px;
1504
}
1505

  
1506
.s14 {
1507
  width: 14px;
1508
  height: 14px;
1509
}
1510

  
1511
.s12 {
1512
  width: 12px;
1513
  height: 12px;
1514
}
1515

  
1500 1516
/***** Icons *****/
1501
.icon {
1517
.icon:not(.icon-svg) {
1502 1518
  background-position: 0% 50%;
1503 1519
  background-repeat: no-repeat;
1504 1520
  padding-left: 20px;
1505 1521
}
1506
.icon-only {
1507
  background-position: 0% 50%;
1508
  background-repeat: no-repeat;
1509
  padding-left: 16px;
1510
  display: inline-block;
1511
  width: 0;
1512
  height: 16px;
1513
  overflow: hidden;
1514
  padding-top: 0;
1515
  padding-bottom: 0;
1516
  font-size: 8px;
1517
  vertical-align: middle;
1522
.icon.icon-svg {
1523
  display: inline-flex;
1518 1524
}
1519
.icon-only::after {
1520
  content: "\a0";
1525

  
1526
a svg, .icon svg {
1527
  fill: #169;
1528
  margin-right: 4px;
1521 1529
}
1522 1530

  
1523
.icon-add { background-image: url(../images/add.png); }
1524
.icon-edit { background-image: url(../images/edit.png); }
1525
.icon-copy { background-image: url(../images/copy.png); }
1531

  
1532
/*.icon-fav svg { fill: #fc8c12; }*/
1533

  
1534
.icon-add:not(.icon-svg) { background-image: url(../images/add.png); }
1535
.icon-edit:not(.icon-svg) { background-image: url(../images/edit.png); }
1536
.icon-copy:not(.icon-svg) { background-image: url(../images/copy.png); }
1526 1537
.icon-duplicate { background-image: url(../images/duplicate.png); }
1527
.icon-del { background-image: url(../images/delete.png); }
1538
.icon-del:not(.icon-svg) { background-image: url(../images/delete.png); }
1528 1539
.icon-move { background-image: url(../images/move.png); }
1529 1540
.icon-save { background-image: url(../images/save.png); }
1530
.icon-download { background-image: url(../images/download.png); }
1541
.icon-download:not(.icon-svg) { background-image: url(../images/download.png); }
1531 1542
.icon-cancel { background-image: url(../images/cancel.png); }
1532 1543
.icon-multiple { background-image: url(../images/table_multiple.png); }
1533 1544
.icon-folder { background-image: url(../images/folder.png); }
......
1536 1547
.icon-user { background-image: url(../images/user.png); }
1537 1548
.icon-project, .icon-projects { background-image: url(../images/projects.png); }
1538 1549
.icon-help { background-image: url(../images/help.png); }
1539
.icon-attachment  { background-image: url(../images/attachment.png); }
1550
.icon-attachment:not(.icon-svg)  { background-image: url(../images/attachment.png); }
1540 1551
.icon-history  { background-image: url(../images/history.png); }
1541 1552
.icon-time-entry, .icon-time  { background-image: url(../images/time.png); }
1542
.icon-time-add  { background-image: url(../images/time_add.png); }
1553
.icon-time-add:not(.icon-svg)  { background-image: url(../images/time_add.png); }
1543 1554
.icon-stats  { background-image: url(../images/stats.png); }
1544 1555
.icon-warning  { background-image: url(../images/warning.png); }
1545 1556
.icon-error { background-image: url(../images/exclamation.png); }
1546
.icon-fav  { background-image: url(../images/fav.png); }
1547
.icon-fav-off  { background-image: url(../images/fav_off.png); }
1557
.icon-fav:not(.icon-svg)  { background-image: url(../images/fav.png); }
1558
.icon-fav-off:not(.icon-svg)  { background-image: url(../images/fav_off.png); }
1548 1559
.icon-reload  { background-image: url(../images/reload.png); }
1549 1560
.icon-lock, .icon-locked  { background-image: url(../images/locked.png); }
1550 1561
.icon-unlock  { background-image: url(../images/unlock.png); }
1551 1562
.icon-checked  { background-image: url(../images/toggle_check.png); }
1552 1563
.icon-report  { background-image: url(../images/report.png); }
1553
.icon-comment, .icon-comments  { background-image: url(../images/comment.png); }
1564
.icon-comment:not(.icon-svg), .icon-comments  { background-image: url(../images/comment.png); }
1554 1565
.icon-summary  { background-image: url(../images/lightning.png); }
1555 1566
.icon-server-authentication { background-image: url(../images/server_key.png); }
1556 1567
.icon-issue { background-image: url(../images/ticket.png); }
......
1586 1597
.icon-project { background-image: url(../images/projects.png); }
1587 1598
.icon-add-bullet { background-image: url(../images/bullet_add.png); }
1588 1599
.icon-shared { background-image: url(../images/link.png); }
1589
.icon-actions { background-image: url(../images/3_bullets.png); }
1600
.icon-actions:not(.icon-svg) { background-image: url(../images/3_bullets.png); }
1590 1601
.icon-sort-handle { background-image: url(../images/reorder.png); }
1591 1602
.icon-expended { background-image: url(../images/arrow_down.png); }
1592 1603
.icon-collapsed { background-image: url(../images/arrow_right.png); }
......
1618 1629
.icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
1619 1630
.icon-file.application-zip { background-image: url(../images/files/zip.png); }
1620 1631
.icon-file.application-gzip { background-image: url(../images/files/zip.png); }
1621
.icon-copy-link { background-image: url(../images/copy_link.png); }
1632
.icon-copy-link:not(.icon-svg) { background-image: url(../images/copy_link.png); }
1622 1633

  
1623 1634
.sort-handle.ajax-loading { background-image: url(../images/loading.gif); }
1624 1635
tr.ui-sortable-helper { border:1px solid #e4e4e4; }
public/svgs/copy.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"/></svg>
public/svgs/download.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M216 0h80c13.3 0 24 10.7 24 24v168h87.7c17.8 0 26.7 21.5 14.1 34.1L269.7 378.3c-7.5 7.5-19.8 7.5-27.3 0L90.1 226.1c-12.6-12.6-3.7-34.1 14.1-34.1H192V24c0-13.3 10.7-24 24-24zm296 376v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h146.7l49 49c20.1 20.1 52.5 20.1 72.6 0l49-49H488c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"/></svg>
public/svgs/ellipsis-h.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z"/></svg>
public/svgs/link.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"/></svg>
public/svgs/paperclip.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M43.246 466.142c-58.43-60.289-57.341-157.511 1.386-217.581L254.392 34c44.316-45.332 116.351-45.336 160.671 0 43.89 44.894 43.943 117.329 0 162.276L232.214 383.128c-29.855 30.537-78.633 30.111-107.982-.998-28.275-29.97-27.368-77.473 1.452-106.953l143.743-146.835c6.182-6.314 16.312-6.422 22.626-.241l22.861 22.379c6.315 6.182 6.422 16.312.241 22.626L171.427 319.927c-4.932 5.045-5.236 13.428-.648 18.292 4.372 4.634 11.245 4.711 15.688.165l182.849-186.851c19.613-20.062 19.613-52.725-.011-72.798-19.189-19.627-49.957-19.637-69.154 0L90.39 293.295c-34.763 35.56-35.299 93.12-1.191 128.313 34.01 35.093 88.985 35.137 123.058.286l172.06-175.999c6.177-6.319 16.307-6.433 22.626-.256l22.877 22.364c6.319 6.177 6.434 16.307.256 22.626l-172.06 175.998c-59.576 60.938-155.943 60.216-214.77-.485z"/></svg>
public/svgs/pen-alt.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"/></svg>
public/svgs/plus-circle.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"/></svg>
public/svgs/quote-left.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M464 256h-80v-64c0-35.3 28.7-64 64-64h8c13.3 0 24-10.7 24-24V56c0-13.3-10.7-24-24-24h-8c-88.4 0-160 71.6-160 160v240c0 26.5 21.5 48 48 48h128c26.5 0 48-21.5 48-48V304c0-26.5-21.5-48-48-48zm-288 0H96v-64c0-35.3 28.7-64 64-64h8c13.3 0 24-10.7 24-24V56c0-13.3-10.7-24-24-24h-8C71.6 32 0 103.6 0 192v240c0 26.5 21.5 48 48 48h128c26.5 0 48-21.5 48-48V304c0-26.5-21.5-48-48-48z"/></svg>
public/svgs/star-off.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M528.1 171.5L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6zM388.6 312.3l23.7 138.4L288 385.4l-124.3 65.3 23.7-138.4-100.6-98 139-20.2 62.2-126 62.2 126 139 20.2-100.6 98z"/></svg>
public/svgs/star.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M259.3 17.8L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0z"/></svg>
public/svgs/time-add.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm61.8-104.4l-84.9-61.7c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v141.7l66.8 48.6c5.4 3.9 6.5 11.4 2.6 16.8L334.6 349c-3.9 5.3-11.4 6.5-16.8 2.6z"/></svg>
public/svgs/trash-alt.svg
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M268 416h24a12 12 0 0 0 12-12V188a12 12 0 0 0-12-12h-24a12 12 0 0 0-12 12v216a12 12 0 0 0 12 12zM432 80h-82.41l-34-56.7A48 48 0 0 0 274.41 0H173.59a48 48 0 0 0-41.16 23.3L98.41 80H16A16 16 0 0 0 0 96v16a16 16 0 0 0 16 16h16v336a48 48 0 0 0 48 48h288a48 48 0 0 0 48-48V128h16a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16zM171.84 50.91A6 6 0 0 1 177 48h94a6 6 0 0 1 5.15 2.91L293.61 80H154.39zM368 464H80V128h288zm-212-48h24a12 12 0 0 0 12-12V188a12 12 0 0 0-12-12h-24a12 12 0 0 0-12 12v216a12 12 0 0 0 12 12z"/></svg>
(20-20/49)