rails_icons 1.7.1 → 1.8.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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/app/assets/javascripts/rails_icons/minisearch.min.js +8 -0
- data/app/assets/javascripts/rails_icons/preview/preview.js +392 -0
- data/app/assets/stylesheets/rails_icons/preview.css +169 -0
- data/app/controllers/rails_icons/previews_controller.rb +28 -0
- data/app/models/rails_icons/preview/tags/animated.yml +4 -0
- data/app/models/rails_icons/preview/tags/feather.yml +287 -0
- data/app/models/rails_icons/preview/tags/flags.yml +271 -0
- data/app/models/rails_icons/preview/tags/heroicons.yml +324 -0
- data/app/models/rails_icons/preview/tags/linear.yml +170 -0
- data/app/models/rails_icons/preview/tags/lucide.yml +1703 -0
- data/app/models/rails_icons/preview/tags/phosphor.yml +1512 -0
- data/app/models/rails_icons/preview/tags/radix.yml +332 -0
- data/app/models/rails_icons/preview/tags/sidekickicons.yml +58 -0
- data/app/models/rails_icons/preview/tags/tabler.yml +5021 -0
- data/app/models/rails_icons/preview/tags/weather.yml +219 -0
- data/app/models/rails_icons/preview/tags.rb +14 -0
- data/app/models/rails_icons/preview.rb +53 -0
- data/app/views/rails_icons/previews/show.html.erb +59 -0
- data/config/routes.rb +8 -0
- data/lib/generators/rails_icons/install_generator.rb +8 -0
- data/lib/rails_icons/generate_tags.rb +44 -0
- data/lib/rails_icons/version.rb +1 -1
- data/lib/ruby_lsp/rails_icons/addon.rb +2 -0
- metadata +22 -6
- data/app/assets/svg/rails_icons/icons/animated/bouncing-dots.svg +0 -1
- data/app/assets/svg/rails_icons/icons/animated/faded-spinner.svg +0 -1
- data/app/assets/svg/rails_icons/icons/animated/fading-dots.svg +0 -1
- data/app/assets/svg/rails_icons/icons/animated/trailing-spinner.svg +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d05be67d1c13063b9c5fca713d8ef525f2859bc06052909d5bd2df13bbede6f
|
|
4
|
+
data.tar.gz: 37d0a613b5c7cbeac079203e5cd44337fd338f58f0e2b5ec8ad111cc4897e9b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d6fd9ff135d39bb5b02cb70e7a96b8a12d3f2f31c95c91cc0faab8280a87f54a97752254291bbe61083d409c7a125cef07d53f2ce7a0e2c4fceac28fbf11e66
|
|
7
|
+
data.tar.gz: fdcffc8c5dfcbb944aea21f64994989d207b968764c0e74f9ff59fd5b8cab4294e15a9e4c35fbce918d3e4011d9e17566a0b6ef57c944a9c560c747c5ab2fe25
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -46,6 +46,8 @@ rails generate rails_icons:install --library=heroicons
|
|
|
46
46
|
rails generate rails_icons:install --libraries=heroicons lucide
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
The generator also mounts an icon preview at `/rails_icons` where you can browse and search all your available icons. This route is open by default, so restrict it in production if needed.
|
|
50
|
+
|
|
49
51
|
|
|
50
52
|
## Usage
|
|
51
53
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minified by jsDelivr using Terser v5.39.0.
|
|
3
|
+
* Original file: /npm/minisearch@7.2.0/dist/umd/index.js
|
|
4
|
+
*
|
|
5
|
+
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
|
6
|
+
*/
|
|
7
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).MiniSearch=e()}(this,(function(){"use strict";const t="KEYS",e="VALUES",s="";class i{constructor(t,e){const s=t._tree,i=Array.from(s.keys());this.set=t,this._type=e,this._path=i.length>0?[{node:s,keys:i}]:[]}next(){const t=this.dive();return this.backtrack(),t}dive(){if(0===this._path.length)return{done:!0,value:void 0};const{node:t,keys:e}=n(this._path);if(n(e)===s)return{done:!1,value:this.result()};const i=t.get(n(e));return this._path.push({node:i,keys:Array.from(i.keys())}),this.dive()}backtrack(){if(0===this._path.length)return;const t=n(this._path).keys;t.pop(),t.length>0||(this._path.pop(),this.backtrack())}key(){return this.set._prefix+this._path.map((({keys:t})=>n(t))).filter((t=>t!==s)).join("")}value(){return n(this._path).node.get(s)}result(){switch(this._type){case e:return this.value();case t:return this.key();default:return[this.key(),this.value()]}}[Symbol.iterator](){return this}}const n=t=>t[t.length-1],o=(t,e,i,n,r,c,h,u)=>{const d=c*h;t:for(const a of t.keys())if(a===s){const e=r[d-1];e<=i&&n.set(u,[t.get(a),e])}else{let s=c;for(let t=0;t<a.length;++t,++s){const n=a[t],o=h*s,c=o-h;let u=r[o];const d=Math.max(0,s-i-1),l=Math.min(h-1,s+i);for(let t=d;t<l;++t){const s=n!==e[t],i=r[c+t]+ +s,h=r[c+t+1]+1,d=r[o+t]+1,a=r[o+t+1]=Math.min(i,h,d);a<u&&(u=a)}if(u>i)continue t}o(t.get(a),e,i,n,r,s,h,u+a)}};class r{constructor(t=new Map,e=""){this._size=void 0,this._tree=t,this._prefix=e}atPrefix(t){if(!t.startsWith(this._prefix))throw new Error("Mismatched prefix");const[e,i]=c(this._tree,t.slice(this._prefix.length));if(void 0===e){const[e,n]=f(i);for(const i of e.keys())if(i!==s&&i.startsWith(n)){const s=new Map;return s.set(i.slice(n.length),e.get(i)),new r(s,t)}}return new r(e,t)}clear(){this._size=void 0,this._tree.clear()}delete(t){return this._size=void 0,d(this._tree,t)}entries(){return new i(this,"ENTRIES")}forEach(t){for(const[e,s]of this)t(e,s,this)}fuzzyGet(t,e){return((t,e,s)=>{const i=new Map;if(void 0===e)return i;const n=e.length+1,r=n+s,c=new Uint8Array(r*n).fill(s+1);for(let t=0;t<n;++t)c[t]=t;for(let t=1;t<r;++t)c[t*n]=t;return o(t,e,s,i,c,1,n,""),i})(this._tree,t,e)}get(t){const e=h(this._tree,t);return void 0!==e?e.get(s):void 0}has(t){const e=h(this._tree,t);return void 0!==e&&e.has(s)}keys(){return new i(this,t)}set(t,e){if("string"!=typeof t)throw new Error("key must be a string");this._size=void 0;return u(this._tree,t).set(s,e),this}get size(){if(this._size)return this._size;this._size=0;const t=this.entries();for(;!t.next().done;)this._size+=1;return this._size}update(t,e){if("string"!=typeof t)throw new Error("key must be a string");this._size=void 0;const i=u(this._tree,t);return i.set(s,e(i.get(s))),this}fetch(t,e){if("string"!=typeof t)throw new Error("key must be a string");this._size=void 0;const i=u(this._tree,t);let n=i.get(s);return void 0===n&&i.set(s,n=e()),n}values(){return new i(this,e)}[Symbol.iterator](){return this.entries()}static from(t){const e=new r;for(const[s,i]of t)e.set(s,i);return e}static fromObject(t){return r.from(Object.entries(t))}}const c=(t,e,i=[])=>{if(0===e.length||null==t)return[t,i];for(const n of t.keys())if(n!==s&&e.startsWith(n))return i.push([t,n]),c(t.get(n),e.slice(n.length),i);return i.push([t,e]),c(void 0,"",i)},h=(t,e)=>{if(0===e.length||null==t)return t;for(const i of t.keys())if(i!==s&&e.startsWith(i))return h(t.get(i),e.slice(i.length))},u=(t,e)=>{const i=e.length;t:for(let n=0;t&&n<i;){for(const o of t.keys())if(o!==s&&e[n]===o[0]){const s=Math.min(i-n,o.length);let r=1;for(;r<s&&e[n+r]===o[r];)++r;const c=t.get(o);if(r===o.length)t=c;else{const s=new Map;s.set(o.slice(r),c),t.set(e.slice(n,n+r),s),t.delete(o),t=s}n+=r;continue t}const o=new Map;return t.set(e.slice(n),o),o}return t},d=(t,e)=>{const[i,n]=c(t,e);if(void 0!==i)if(i.delete(s),0===i.size)a(n);else if(1===i.size){const[t,e]=i.entries().next().value;l(n,t,e)}},a=t=>{if(0===t.length)return;const[e,i]=f(t);if(e.delete(i),0===e.size)a(t.slice(0,-1));else if(1===e.size){const[i,n]=e.entries().next().value;i!==s&&l(t.slice(0,-1),i,n)}},l=(t,e,s)=>{if(0===t.length)return;const[i,n]=f(t);i.set(n+e,s),i.delete(n)},f=t=>t[t.length-1],m="or",_="and",g="and_not";class p{constructor(t){if(null==(null==t?void 0:t.fields))throw new Error('MiniSearch: option "fields" must be provided');const e=null==t.autoVacuum||!0===t.autoVacuum?M:t.autoVacuum;this._options={...z,...t,autoVacuum:e,searchOptions:{...b,...t.searchOptions||{}},autoSuggestOptions:{...I,...t.autoSuggestOptions||{}}},this._index=new r,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldIds={},this._fieldLength=new Map,this._avgFieldLength=[],this._nextId=0,this._storedFields=new Map,this._dirtCount=0,this._currentVacuum=null,this._enqueuedVacuum=null,this._enqueuedVacuumConditions=F,this.addFields(this._options.fields)}add(t){const{extractField:e,stringifyField:s,tokenize:i,processTerm:n,fields:o,idField:r}=this._options,c=e(t,r);if(null==c)throw new Error(`MiniSearch: document does not have ID field "${r}"`);if(this._idToShortId.has(c))throw new Error(`MiniSearch: duplicate ID ${c}`);const h=this.addDocumentId(c);this.saveStoredFields(h,t);for(const r of o){const o=e(t,r);if(null==o)continue;const c=i(s(o,r),r),u=this._fieldIds[r],d=new Set(c).size;this.addFieldLength(h,u,this._documentCount-1,d);for(const t of c){const e=n(t,r);if(Array.isArray(e))for(const t of e)this.addTerm(u,h,t);else e&&this.addTerm(u,h,e)}}}addAll(t){for(const e of t)this.add(e)}addAllAsync(t,e={}){const{chunkSize:s=10}=e,i={chunk:[],promise:Promise.resolve()},{chunk:n,promise:o}=t.reduce((({chunk:t,promise:e},i,n)=>(t.push(i),(n+1)%s==0?{chunk:[],promise:e.then((()=>new Promise((t=>setTimeout(t,0))))).then((()=>this.addAll(t)))}:{chunk:t,promise:e})),i);return o.then((()=>this.addAll(n)))}remove(t){const{tokenize:e,processTerm:s,extractField:i,stringifyField:n,fields:o,idField:r}=this._options,c=i(t,r);if(null==c)throw new Error(`MiniSearch: document does not have ID field "${r}"`);const h=this._idToShortId.get(c);if(null==h)throw new Error(`MiniSearch: cannot remove document with ID ${c}: it is not in the index`);for(const r of o){const o=i(t,r);if(null==o)continue;const c=e(n(o,r),r),u=this._fieldIds[r],d=new Set(c).size;this.removeFieldLength(h,u,this._documentCount,d);for(const t of c){const e=s(t,r);if(Array.isArray(e))for(const t of e)this.removeTerm(u,h,t);else e&&this.removeTerm(u,h,e)}}this._storedFields.delete(h),this._documentIds.delete(h),this._idToShortId.delete(c),this._fieldLength.delete(h),this._documentCount-=1}removeAll(t){if(t)for(const e of t)this.remove(e);else{if(arguments.length>0)throw new Error("Expected documents to be present. Omit the argument to remove all documents.");this._index=new r,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldLength=new Map,this._avgFieldLength=[],this._storedFields=new Map,this._nextId=0}}discard(t){const e=this._idToShortId.get(t);if(null==e)throw new Error(`MiniSearch: cannot discard document with ID ${t}: it is not in the index`);this._idToShortId.delete(t),this._documentIds.delete(e),this._storedFields.delete(e),(this._fieldLength.get(e)||[]).forEach(((t,s)=>{this.removeFieldLength(e,s,this._documentCount,t)})),this._fieldLength.delete(e),this._documentCount-=1,this._dirtCount+=1,this.maybeAutoVacuum()}maybeAutoVacuum(){if(!1===this._options.autoVacuum)return;const{minDirtFactor:t,minDirtCount:e,batchSize:s,batchWait:i}=this._options.autoVacuum;this.conditionalVacuum({batchSize:s,batchWait:i},{minDirtCount:e,minDirtFactor:t})}discardAll(t){const e=this._options.autoVacuum;try{this._options.autoVacuum=!1;for(const e of t)this.discard(e)}finally{this._options.autoVacuum=e}this.maybeAutoVacuum()}replace(t){const{idField:e,extractField:s}=this._options,i=s(t,e);this.discard(i),this.add(t)}vacuum(t={}){return this.conditionalVacuum(t)}conditionalVacuum(t,e){return this._currentVacuum?(this._enqueuedVacuumConditions=this._enqueuedVacuumConditions&&e,null!=this._enqueuedVacuum||(this._enqueuedVacuum=this._currentVacuum.then((()=>{const e=this._enqueuedVacuumConditions;return this._enqueuedVacuumConditions=F,this.performVacuuming(t,e)}))),this._enqueuedVacuum):!1===this.vacuumConditionsMet(e)?Promise.resolve():(this._currentVacuum=this.performVacuuming(t),this._currentVacuum)}async performVacuuming(t,e){const s=this._dirtCount;if(this.vacuumConditionsMet(e)){const e=t.batchSize||S.batchSize,i=t.batchWait||S.batchWait;let n=1;for(const[t,s]of this._index){for(const[t,e]of s)for(const[i]of e)this._documentIds.has(i)||(e.size<=1?s.delete(t):e.delete(i));0===this._index.get(t).size&&this._index.delete(t),n%e==0&&await new Promise((t=>setTimeout(t,i))),n+=1}this._dirtCount-=s}await null,this._currentVacuum=this._enqueuedVacuum,this._enqueuedVacuum=null}vacuumConditionsMet(t){if(null==t)return!0;let{minDirtCount:e,minDirtFactor:s}=t;return e=e||M.minDirtCount,s=s||M.minDirtFactor,this.dirtCount>=e&&this.dirtFactor>=s}get isVacuuming(){return null!=this._currentVacuum}get dirtCount(){return this._dirtCount}get dirtFactor(){return this._dirtCount/(1+this._documentCount+this._dirtCount)}has(t){return this._idToShortId.has(t)}getStoredFields(t){const e=this._idToShortId.get(t);if(null!=e)return this._storedFields.get(e)}search(t,e={}){const{searchOptions:s}=this._options,i={...s,...e},n=this.executeQuery(t,e),o=[];for(const[t,{score:e,terms:s,match:r}]of n){const n=s.length||1,c={id:this._documentIds.get(t),score:e*n,terms:Object.keys(r),queryTerms:s,match:r};Object.assign(c,this._storedFields.get(t)),(null==i.filter||i.filter(c))&&o.push(c)}return t===p.wildcard&&null==i.boostDocument||o.sort(V),o}autoSuggest(t,e={}){e={...this._options.autoSuggestOptions,...e};const s=new Map;for(const{score:i,terms:n}of this.search(t,e)){const t=n.join(" "),e=s.get(t);null!=e?(e.score+=i,e.count+=1):s.set(t,{score:i,terms:n,count:1})}const i=[];for(const[t,{score:e,terms:n,count:o}]of s)i.push({suggestion:t,terms:n,score:e/o});return i.sort(V),i}get documentCount(){return this._documentCount}get termCount(){return this._index.size}static loadJSON(t,e){if(null==e)throw new Error("MiniSearch: loadJSON should be given the same options used when serializing the index");return this.loadJS(JSON.parse(t),e)}static async loadJSONAsync(t,e){if(null==e)throw new Error("MiniSearch: loadJSON should be given the same options used when serializing the index");return this.loadJSAsync(JSON.parse(t),e)}static getDefault(t){if(z.hasOwnProperty(t))return w(z,t);throw new Error(`MiniSearch: unknown option "${t}"`)}static loadJS(t,e){const{index:s,documentIds:i,fieldLength:n,storedFields:o,serializationVersion:r}=t,c=this.instantiateMiniSearch(t,e);c._documentIds=T(i),c._fieldLength=T(n),c._storedFields=T(o);for(const[t,e]of c._documentIds)c._idToShortId.set(e,t);for(const[t,e]of s){const s=new Map;for(const t of Object.keys(e)){let i=e[t];1===r&&(i=i.ds),s.set(parseInt(t,10),T(i))}c._index.set(t,s)}return c}static async loadJSAsync(t,e){const{index:s,documentIds:i,fieldLength:n,storedFields:o,serializationVersion:r}=t,c=this.instantiateMiniSearch(t,e);c._documentIds=await L(i),c._fieldLength=await L(n),c._storedFields=await L(o);for(const[t,e]of c._documentIds)c._idToShortId.set(e,t);let h=0;for(const[t,e]of s){const s=new Map;for(const t of Object.keys(e)){let i=e[t];1===r&&(i=i.ds),s.set(parseInt(t,10),await L(i))}++h%1e3==0&&await E(0),c._index.set(t,s)}return c}static instantiateMiniSearch(t,e){const{documentCount:s,nextId:i,fieldIds:n,averageFieldLength:o,dirtCount:c,serializationVersion:h}=t;if(1!==h&&2!==h)throw new Error("MiniSearch: cannot deserialize an index created with an incompatible version");const u=new p(e);return u._documentCount=s,u._nextId=i,u._idToShortId=new Map,u._fieldIds=n,u._avgFieldLength=o,u._dirtCount=c||0,u._index=new r,u}executeQuery(t,e={}){if(t===p.wildcard)return this.executeWildcardQuery(e);if("string"!=typeof t){const s={...e,...t,queries:void 0},i=t.queries.map((t=>this.executeQuery(t,s)));return this.combineResults(i,s.combineWith)}const{tokenize:s,processTerm:i,searchOptions:n}=this._options,o={tokenize:s,processTerm:i,...n,...e},{tokenize:r,processTerm:c}=o,h=r(t).flatMap((t=>c(t))).filter((t=>!!t)).map(x(o)).map((t=>this.executeQuerySpec(t,o)));return this.combineResults(h,o.combineWith)}executeQuerySpec(t,e){const s={...this._options.searchOptions,...e},i=(s.fields||this._options.fields).reduce(((t,e)=>({...t,[e]:w(s.boost,e)||1})),{}),{boostDocument:n,weights:o,maxFuzzy:r,bm25:c}=s,{fuzzy:h,prefix:u}={...b.weights,...o},d=this._index.get(t.term),a=this.termResults(t.term,t.term,1,t.termBoost,d,i,n,c);let l,f;if(t.prefix&&(l=this._index.atPrefix(t.term)),t.fuzzy){const e=!0===t.fuzzy?.2:t.fuzzy,s=e<1?Math.min(r,Math.round(t.term.length*e)):e;s&&(f=this._index.fuzzyGet(t.term,s))}if(l)for(const[e,s]of l){const o=e.length-t.term.length;if(!o)continue;null==f||f.delete(e);const r=u*e.length/(e.length+.3*o);this.termResults(t.term,e,r,t.termBoost,s,i,n,c,a)}if(f)for(const e of f.keys()){const[s,o]=f.get(e);if(!o)continue;const r=h*e.length/(e.length+o);this.termResults(t.term,e,r,t.termBoost,s,i,n,c,a)}return a}executeWildcardQuery(t){const e=new Map,s={...this._options.searchOptions,...t};for(const[t,i]of this._documentIds){const n=s.boostDocument?s.boostDocument(i,"",this._storedFields.get(t)):1;e.set(t,{score:n,terms:[],match:{}})}return e}combineResults(t,e=m){if(0===t.length)return new Map;const s=e.toLowerCase(),i=y[s];if(!i)throw new Error(`Invalid combination operator: ${e}`);return t.reduce(i)||new Map}toJSON(){const t=[];for(const[e,s]of this._index){const i={};for(const[t,e]of s)i[t]=Object.fromEntries(e);t.push([e,i])}return{documentCount:this._documentCount,nextId:this._nextId,documentIds:Object.fromEntries(this._documentIds),fieldIds:this._fieldIds,fieldLength:Object.fromEntries(this._fieldLength),averageFieldLength:this._avgFieldLength,storedFields:Object.fromEntries(this._storedFields),dirtCount:this._dirtCount,index:t,serializationVersion:2}}termResults(t,e,s,i,n,o,r,c,h=new Map){if(null==n)return h;for(const u of Object.keys(o)){const d=o[u],a=this._fieldIds[u],l=n.get(a);if(null==l)continue;let f=l.size;const m=this._avgFieldLength[a];for(const n of l.keys()){if(!this._documentIds.has(n)){this.removeTerm(a,n,e),f-=1;continue}const o=r?r(this._documentIds.get(n),e,this._storedFields.get(n)):1;if(!o)continue;const _=l.get(n),g=this._fieldLength.get(n)[a],p=s*i*d*o*v(_,f,this._documentCount,g,m,c),y=h.get(n);if(y){y.score+=p,k(y.terms,t);const s=w(y.match,e);s?s.push(u):y.match[e]=[u]}else h.set(n,{score:p,terms:[t],match:{[e]:[u]}})}}return h}addTerm(t,e,s){const i=this._index.fetch(s,O);let n=i.get(t);if(null==n)n=new Map,n.set(e,1),i.set(t,n);else{const t=n.get(e);n.set(e,(t||0)+1)}}removeTerm(t,e,s){if(!this._index.has(s))return void this.warnDocumentChanged(e,t,s);const i=this._index.fetch(s,O),n=i.get(t);null==n||null==n.get(e)?this.warnDocumentChanged(e,t,s):n.get(e)<=1?n.size<=1?i.delete(t):n.delete(e):n.set(e,n.get(e)-1),0===this._index.get(s).size&&this._index.delete(s)}warnDocumentChanged(t,e,s){for(const i of Object.keys(this._fieldIds))if(this._fieldIds[i]===e)return void this._options.logger("warn",`MiniSearch: document with ID ${this._documentIds.get(t)} has changed before removal: term "${s}" was not present in field "${i}". Removing a document after it has changed can corrupt the index!`,"version_conflict")}addDocumentId(t){const e=this._nextId;return this._idToShortId.set(t,e),this._documentIds.set(e,t),this._documentCount+=1,this._nextId+=1,e}addFields(t){for(let e=0;e<t.length;e++)this._fieldIds[t[e]]=e}addFieldLength(t,e,s,i){let n=this._fieldLength.get(t);null==n&&this._fieldLength.set(t,n=[]),n[e]=i;const o=(this._avgFieldLength[e]||0)*s+i;this._avgFieldLength[e]=o/(s+1)}removeFieldLength(t,e,s,i){if(1===s)return void(this._avgFieldLength[e]=0);const n=this._avgFieldLength[e]*s-i;this._avgFieldLength[e]=n/(s-1)}saveStoredFields(t,e){const{storeFields:s,extractField:i}=this._options;if(null==s||0===s.length)return;let n=this._storedFields.get(t);null==n&&this._storedFields.set(t,n={});for(const t of s){const s=i(e,t);void 0!==s&&(n[t]=s)}}}p.wildcard=Symbol("*");const w=(t,e)=>Object.prototype.hasOwnProperty.call(t,e)?t[e]:void 0,y={[m]:(t,e)=>{for(const s of e.keys()){const i=t.get(s);if(null==i)t.set(s,e.get(s));else{const{score:t,terms:n,match:o}=e.get(s);i.score=i.score+t,i.match=Object.assign(i.match,o),C(i.terms,n)}}return t},[_]:(t,e)=>{const s=new Map;for(const i of e.keys()){const n=t.get(i);if(null==n)continue;const{score:o,terms:r,match:c}=e.get(i);C(n.terms,r),s.set(i,{score:n.score+o,terms:n.terms,match:Object.assign(n.match,c)})}return s},[g]:(t,e)=>{for(const s of e.keys())t.delete(s);return t}},v=(t,e,s,i,n,o)=>{const{k:r,b:c,d:h}=o;return Math.log(1+(s-e+.5)/(e+.5))*(h+t*(r+1)/(t+r*(1-c+c*i/n)))},x=t=>(e,s,i)=>({term:e,fuzzy:"function"==typeof t.fuzzy?t.fuzzy(e,s,i):t.fuzzy||!1,prefix:"function"==typeof t.prefix?t.prefix(e,s,i):!0===t.prefix,termBoost:"function"==typeof t.boostTerm?t.boostTerm(e,s,i):1}),z={idField:"id",extractField:(t,e)=>t[e],stringifyField:(t,e)=>t.toString(),tokenize:t=>t.split(D),processTerm:t=>t.toLowerCase(),fields:void 0,searchOptions:void 0,storeFields:[],logger:(t,e)=>{"function"==typeof(null===console||void 0===console?void 0:console[t])&&console[t](e)},autoVacuum:!0},b={combineWith:m,prefix:!1,fuzzy:!1,maxFuzzy:6,boost:{},weights:{fuzzy:.45,prefix:.375},bm25:{k:1.2,b:.7,d:.5}},I={combineWith:"and",prefix:(t,e,s)=>e===s.length-1},S={batchSize:1e3,batchWait:10},F={minDirtFactor:.1,minDirtCount:20},M={...S,...F},k=(t,e)=>{t.includes(e)||t.push(e)},C=(t,e)=>{for(const s of e)t.includes(s)||t.push(s)},V=({score:t},{score:e})=>e-t,O=()=>new Map,T=t=>{const e=new Map;for(const s of Object.keys(t))e.set(parseInt(s,10),t[s]);return e},L=async t=>{const e=new Map;let s=0;for(const i of Object.keys(t))e.set(parseInt(i,10),t[i]),++s%1e3==0&&await E(0);return e},E=t=>new Promise((e=>setTimeout(e,t))),D=/[\n\r\p{Z}\p{P}]+/u;return p}));
|
|
8
|
+
//# sourceMappingURL=/sm/1e4d3c573a14b2678ad5755d10141fbd85cba14ae591344bb984b98576ea9860.map
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
class RailsIconPreview extends HTMLElement {
|
|
2
|
+
connectedCallback() {
|
|
3
|
+
this.#render()
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
get searchData() {
|
|
7
|
+
return {
|
|
8
|
+
id: this.#name,
|
|
9
|
+
name: this.#name,
|
|
10
|
+
tags: this.getAttribute('tags') || ''
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#render() {
|
|
15
|
+
this.innerHTML = `
|
|
16
|
+
<section>
|
|
17
|
+
<div class='svg'>${this.#svgIcon}</div>
|
|
18
|
+
<p title='${this.#name}'>${this.#name}</p>
|
|
19
|
+
|
|
20
|
+
<div class='actions'>
|
|
21
|
+
<button data-action='copy-name'>Copy name</button>
|
|
22
|
+
|
|
23
|
+
<button data-action='copy-helper'>Copy helper</button>
|
|
24
|
+
</div>
|
|
25
|
+
</section>
|
|
26
|
+
`
|
|
27
|
+
|
|
28
|
+
this.querySelector('[data-action="copy-name"]').onclick = () => this.#copyName()
|
|
29
|
+
this.querySelector('[data-action="copy-helper"]').onclick = () => this.#copyHelper()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#copyName() {
|
|
33
|
+
navigator.clipboard.writeText(this.#name)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#copyHelper() {
|
|
37
|
+
const parts = [`icon('${this.#name}'`]
|
|
38
|
+
|
|
39
|
+
if (this.#library !== this.#defaultLibrary) {
|
|
40
|
+
parts.push(`library: :${this.#library}`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (this.#variant && this.#variant !== this.#defaultVariant) {
|
|
44
|
+
parts.push(`variant: :${this.#variant}`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const helper = parts.length > 1 ? `${parts[0]}, ${parts.slice(1).join(', ')})` : `${parts[0]})`
|
|
48
|
+
navigator.clipboard.writeText(helper)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get #name() {
|
|
52
|
+
return this.getAttribute('name')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get #library() {
|
|
56
|
+
return this.getAttribute('library')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get #variant() {
|
|
60
|
+
return this.getAttribute('variant')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get #defaultLibrary() {
|
|
64
|
+
return this.getAttribute('default-library')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get #defaultVariant() {
|
|
68
|
+
return this.getAttribute('default-variant')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get #svgIcon() {
|
|
72
|
+
return this.innerHTML
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
customElements.define('rails-icon-preview', RailsIconPreview)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class IconSearch {
|
|
80
|
+
constructor() {
|
|
81
|
+
this.searchInput = document.querySelector('input[type="search"]')
|
|
82
|
+
this.resultCount = document.querySelector('.result-count')
|
|
83
|
+
this.icons = Array.from(document.querySelectorAll('rails-icon-preview'))
|
|
84
|
+
this.totalCount = this.icons.length
|
|
85
|
+
|
|
86
|
+
this.miniSearch = new MiniSearch({
|
|
87
|
+
fields: ['name', 'tags'],
|
|
88
|
+
storeFields: ['id'],
|
|
89
|
+
searchOptions: {
|
|
90
|
+
fuzzy: false,
|
|
91
|
+
prefix: false
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
this.#indexIcons()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getIconList() {
|
|
99
|
+
return document.querySelector('ul.icons')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getVisibleIcons() {
|
|
103
|
+
return this.icons.filter(icon => icon.closest('li').style.display !== 'none')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#indexIcons() {
|
|
107
|
+
const documents = this.icons.map(icon => icon.searchData)
|
|
108
|
+
|
|
109
|
+
this.miniSearch.addAll(documents)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
filterIcons(query) {
|
|
113
|
+
if (!query) {
|
|
114
|
+
this.icons.forEach(icon => {
|
|
115
|
+
icon.closest('li').style.display = ''
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
this.#updateResultCount(this.totalCount)
|
|
119
|
+
this.#showNoResults(false)
|
|
120
|
+
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const results = this.miniSearch.search(query)
|
|
125
|
+
const resultIds = new Set(results.map(result => result.id))
|
|
126
|
+
let visibleCount = 0
|
|
127
|
+
|
|
128
|
+
this.icons.forEach(icon => {
|
|
129
|
+
const listItem = icon.closest('li')
|
|
130
|
+
|
|
131
|
+
if (resultIds.has(icon.searchData.id)) {
|
|
132
|
+
listItem.style.display = ''
|
|
133
|
+
|
|
134
|
+
visibleCount++
|
|
135
|
+
} else {
|
|
136
|
+
listItem.style.display = 'none'
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
this.#updateResultCount(visibleCount)
|
|
141
|
+
this.#showNoResults(visibleCount === 0, query)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#updateResultCount(count) {
|
|
145
|
+
if (this.resultCount) {
|
|
146
|
+
this.resultCount.textContent = count === this.totalCount
|
|
147
|
+
? ''
|
|
148
|
+
: `${count} of ${this.totalCount} icons`
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#showNoResults(show, query = '') {
|
|
153
|
+
const noResults = document.querySelector('.no-results')
|
|
154
|
+
const iconsList = document.querySelector('ul.icons')
|
|
155
|
+
|
|
156
|
+
if (show) {
|
|
157
|
+
noResults.removeAttribute('hidden')
|
|
158
|
+
noResults.querySelector('.query').textContent = query
|
|
159
|
+
|
|
160
|
+
iconsList.setAttribute('hidden', 'hidden')
|
|
161
|
+
} else {
|
|
162
|
+
noResults.setAttribute('hidden', 'hidden')
|
|
163
|
+
|
|
164
|
+
iconsList.removeAttribute('hidden')
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class UrlSync {
|
|
171
|
+
constructor({ onSearch }) {
|
|
172
|
+
this.onSearch = onSearch
|
|
173
|
+
|
|
174
|
+
this.#loadFromUrl()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
#loadFromUrl() {
|
|
178
|
+
const params = new URLSearchParams(window.location.search)
|
|
179
|
+
const query = params.get('q')
|
|
180
|
+
|
|
181
|
+
if (query) {
|
|
182
|
+
this.onSearch?.(query)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
updateUrl(query) {
|
|
187
|
+
const url = new URL(window.location)
|
|
188
|
+
|
|
189
|
+
if (query) {
|
|
190
|
+
url.searchParams.set('q', query)
|
|
191
|
+
} else {
|
|
192
|
+
url.searchParams.delete('q')
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
history.replaceState(null, '', url)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class KeyboardController {
|
|
201
|
+
constructor({ searchInput, iconList, iconSearch, onFilter, onClearFocus, onActivate }) {
|
|
202
|
+
this.searchInput = searchInput
|
|
203
|
+
this.iconList = iconList
|
|
204
|
+
this.iconSearch = iconSearch
|
|
205
|
+
|
|
206
|
+
this.onFilter = onFilter
|
|
207
|
+
this.onClearFocus = onClearFocus
|
|
208
|
+
this.onActivate = onActivate
|
|
209
|
+
|
|
210
|
+
this.focusedIndex = -1
|
|
211
|
+
|
|
212
|
+
this.#setup()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
#setup() {
|
|
216
|
+
this.#setupGlobalKeys()
|
|
217
|
+
this.#setupSearchInputKeys()
|
|
218
|
+
this.#setupIconListKeys()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
#setupGlobalKeys() {
|
|
222
|
+
document.addEventListener('keydown', (event) => {
|
|
223
|
+
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') return
|
|
224
|
+
|
|
225
|
+
if (event.key === '/') {
|
|
226
|
+
event.preventDefault()
|
|
227
|
+
|
|
228
|
+
this.searchInput?.focus()
|
|
229
|
+
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key)) {
|
|
234
|
+
this.searchInput?.focus()
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#setupSearchInputKeys() {
|
|
240
|
+
this.searchInput?.addEventListener('keydown', (event) => {
|
|
241
|
+
switch (event.key) {
|
|
242
|
+
case 'Escape':
|
|
243
|
+
this.searchInput.value = ''
|
|
244
|
+
this.searchInput.blur()
|
|
245
|
+
this.onFilter('')
|
|
246
|
+
this.#clearFocus()
|
|
247
|
+
|
|
248
|
+
break
|
|
249
|
+
case 'ArrowDown':
|
|
250
|
+
event.preventDefault()
|
|
251
|
+
|
|
252
|
+
this.#moveFocus(1)
|
|
253
|
+
|
|
254
|
+
break
|
|
255
|
+
case 'ArrowUp':
|
|
256
|
+
event.preventDefault()
|
|
257
|
+
|
|
258
|
+
this.#moveFocus(-1)
|
|
259
|
+
|
|
260
|
+
break
|
|
261
|
+
case 'Enter':
|
|
262
|
+
if (this.focusedIndex >= 0) {
|
|
263
|
+
this.#activateFocused()
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
break
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
this.searchInput?.addEventListener('input', (event) => {
|
|
271
|
+
this.#clearFocus()
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
#setupIconListKeys() {
|
|
276
|
+
this.iconList?.addEventListener('keydown', (event) => {
|
|
277
|
+
switch (event.key) {
|
|
278
|
+
case 'ArrowDown':
|
|
279
|
+
event.preventDefault()
|
|
280
|
+
|
|
281
|
+
this.#moveFocus(1)
|
|
282
|
+
|
|
283
|
+
break
|
|
284
|
+
case 'ArrowUp':
|
|
285
|
+
event.preventDefault()
|
|
286
|
+
|
|
287
|
+
this.#moveFocus(-1)
|
|
288
|
+
|
|
289
|
+
break
|
|
290
|
+
case 'Enter':
|
|
291
|
+
case ' ':
|
|
292
|
+
if (this.focusedIndex >= 0) {
|
|
293
|
+
event.preventDefault()
|
|
294
|
+
|
|
295
|
+
this.#activateFocused()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
break
|
|
299
|
+
case 'Escape':
|
|
300
|
+
this.#clearFocus()
|
|
301
|
+
|
|
302
|
+
this.searchInput?.focus()
|
|
303
|
+
|
|
304
|
+
break
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
setFocusedIndex(index) {
|
|
310
|
+
this.focusedIndex = index
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
#moveFocus(direction) {
|
|
314
|
+
const icons = this.iconSearch?.getVisibleIcons() || []
|
|
315
|
+
if (icons.length === 0) return
|
|
316
|
+
|
|
317
|
+
this.focusedIndex += direction
|
|
318
|
+
|
|
319
|
+
if (this.focusedIndex < 0) {
|
|
320
|
+
this.focusedIndex = icons.length - 1
|
|
321
|
+
} else if (this.focusedIndex >= icons.length) {
|
|
322
|
+
this.focusedIndex = 0
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this.#updateFocus(icons)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
#updateFocus(icons) {
|
|
329
|
+
icons.forEach((icon, index) => {
|
|
330
|
+
icon.closest('li').classList.toggle('focused', index === this.focusedIndex)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
if (icons[this.focusedIndex]) {
|
|
334
|
+
icons[this.focusedIndex].closest('li').scrollIntoView({ block: 'nearest' })
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
#clearFocus() {
|
|
339
|
+
this.focusedIndex = -1
|
|
340
|
+
|
|
341
|
+
const icons = this.iconSearch?.icons || []
|
|
342
|
+
|
|
343
|
+
icons.forEach(icon => icon.closest('li').classList.remove('focused'))
|
|
344
|
+
|
|
345
|
+
this.onClearFocus?.()
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#activateFocused() {
|
|
349
|
+
const icons = this.iconSearch?.getVisibleIcons() || []
|
|
350
|
+
const focusedIcon = icons[this.focusedIndex]
|
|
351
|
+
|
|
352
|
+
if (focusedIcon) {
|
|
353
|
+
focusedIcon.querySelector('[data-action="copy-helper"]')?.click()
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
360
|
+
const searchInput = document.querySelector('input[type="search"]')
|
|
361
|
+
const iconList = document.querySelector('ul.icons')
|
|
362
|
+
const iconSearch = new IconSearch()
|
|
363
|
+
|
|
364
|
+
const urlSync = new UrlSync({
|
|
365
|
+
onSearch: (query) => {
|
|
366
|
+
searchInput.value = query
|
|
367
|
+
|
|
368
|
+
iconSearch.filterIcons(query)
|
|
369
|
+
}
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
const keyboard = new KeyboardController({
|
|
373
|
+
searchInput,
|
|
374
|
+
iconList,
|
|
375
|
+
iconSearch,
|
|
376
|
+
|
|
377
|
+
onFilter: (query) => {
|
|
378
|
+
iconSearch.filterIcons(query)
|
|
379
|
+
urlSync.updateUrl(query)
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
onClearFocus: () => keyboard.setFocusedIndex(-1)
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
searchInput?.addEventListener('input', (event) => {
|
|
386
|
+
const query = event.target.value.trim()
|
|
387
|
+
|
|
388
|
+
iconSearch.filterIcons(query)
|
|
389
|
+
|
|
390
|
+
urlSync.updateUrl(query)
|
|
391
|
+
})
|
|
392
|
+
})
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
body {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 2rem;
|
|
4
|
+
font-family: system-ui, sans-serif;
|
|
5
|
+
background-color: rgb(241 245 249 / .5);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
ul { list-style: none; }
|
|
9
|
+
|
|
10
|
+
nav {
|
|
11
|
+
display: grid;
|
|
12
|
+
grid-template-columns: auto auto 1fr auto;
|
|
13
|
+
gap: 1rem;
|
|
14
|
+
align-items: center;
|
|
15
|
+
margin-bottom: 2rem;
|
|
16
|
+
|
|
17
|
+
select {
|
|
18
|
+
appearance: none;
|
|
19
|
+
padding: .5rem 2rem .5rem .75rem;
|
|
20
|
+
font-size: .875rem;
|
|
21
|
+
color: rgb(30 41 59);
|
|
22
|
+
border: 1px solid rgb(226 232 240);
|
|
23
|
+
border-radius: 0.5rem;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
background-color: rgb(255 255 255);
|
|
26
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
27
|
+
background-repeat: no-repeat;
|
|
28
|
+
background-position: right .5rem center;
|
|
29
|
+
|
|
30
|
+
&:hover {
|
|
31
|
+
border-color: rgb(203 213 225);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&:focus {
|
|
35
|
+
outline: none;
|
|
36
|
+
border-color: rgb(59 130 246);
|
|
37
|
+
box-shadow: 0 0 0 3px rgb(59 130 246 / .1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.search-wrapper {
|
|
42
|
+
position: relative;
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
max-width: 400px;
|
|
46
|
+
width: 100%;
|
|
47
|
+
|
|
48
|
+
input[type="search"] {
|
|
49
|
+
width: 100%;
|
|
50
|
+
padding: .5rem 3rem .5rem .75rem;
|
|
51
|
+
font-size: .875rem;
|
|
52
|
+
color: rgb(30 41 59);
|
|
53
|
+
background-color: rgb(255 255 255);
|
|
54
|
+
border: 1px solid rgb(226 232 240);
|
|
55
|
+
border-radius: .5rem;
|
|
56
|
+
|
|
57
|
+
&::placeholder { color: rgb(148 163 184); }
|
|
58
|
+
|
|
59
|
+
&:hover { border-color: rgb(203 213 225); }
|
|
60
|
+
|
|
61
|
+
&:focus {
|
|
62
|
+
outline: none;
|
|
63
|
+
border-color: rgb(59 130 246);
|
|
64
|
+
box-shadow: 0 0 0 3px rgb(59 130 246 / .1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&::-webkit-search-cancel-button { display: none; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
kbd {
|
|
71
|
+
position: absolute;
|
|
72
|
+
right: .5rem;
|
|
73
|
+
padding: .125rem .375rem;
|
|
74
|
+
font-family: inherit;
|
|
75
|
+
font-size: .75rem;
|
|
76
|
+
color: rgb(148 163 184);
|
|
77
|
+
background-color: rgb(241 245 249);
|
|
78
|
+
border: 1px solid rgb(226 232 240);
|
|
79
|
+
border-radius: .25rem;
|
|
80
|
+
pointer-events: none;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.result-count {
|
|
85
|
+
min-width: 120px;
|
|
86
|
+
font-size: .875rem;
|
|
87
|
+
color: rgb(100 116 139 / .6);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.no-results {
|
|
92
|
+
padding: 2rem;
|
|
93
|
+
font-size: .875rem;
|
|
94
|
+
text-align: center;
|
|
95
|
+
color: rgb(100 116 139 / .6);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
ul.icons {
|
|
99
|
+
margin: 0;
|
|
100
|
+
padding: 0;
|
|
101
|
+
display: grid;
|
|
102
|
+
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
103
|
+
gap: 1rem;
|
|
104
|
+
|
|
105
|
+
li { display: contents; }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
rails-icon-preview {
|
|
109
|
+
section {
|
|
110
|
+
position: relative;
|
|
111
|
+
padding: 1rem .5rem;
|
|
112
|
+
background-color: rgb(255 255 255);
|
|
113
|
+
border-radius: .5rem;
|
|
114
|
+
|
|
115
|
+
.svg {
|
|
116
|
+
width: 32px;
|
|
117
|
+
margin-inline: auto;
|
|
118
|
+
aspect-ratio: 1 / 1;
|
|
119
|
+
|
|
120
|
+
svg { width: 100%; height: 100%; }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
p {
|
|
124
|
+
margin: 0;
|
|
125
|
+
margin-block-start: .5rem;
|
|
126
|
+
font-size: .75rem;
|
|
127
|
+
color: rgb(100 116 139);
|
|
128
|
+
text-align: center;
|
|
129
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.actions {
|
|
133
|
+
position: absolute;
|
|
134
|
+
inset: 0;
|
|
135
|
+
display: grid;
|
|
136
|
+
grid-template-rows: 1fr 1fr;
|
|
137
|
+
gap: .5rem;
|
|
138
|
+
padding: .5rem;
|
|
139
|
+
opacity: 0;
|
|
140
|
+
transition: opacity ease-in-out 100ms;
|
|
141
|
+
|
|
142
|
+
button {
|
|
143
|
+
padding: .25rem .125rem;
|
|
144
|
+
font-size: .75rem;
|
|
145
|
+
font-weight: 500;
|
|
146
|
+
color: rgb(30 41 59);
|
|
147
|
+
background-color: rgb(226 232 240 / .5);
|
|
148
|
+
border: 1px solid rgb(226 232 240 / .5);
|
|
149
|
+
border-radius: 9999px;
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
-webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
|
|
152
|
+
|
|
153
|
+
&:hover {
|
|
154
|
+
border: 1px solid rgb(226 232 240 / .9);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
&:hover .actions,
|
|
160
|
+
&:focus-within .actions {
|
|
161
|
+
opacity: 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
ul.icons li.focused section {
|
|
167
|
+
outline: 2px solid rgb(59 130 246);
|
|
168
|
+
outline-offset: 2px;
|
|
169
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsIcons
|
|
4
|
+
class PreviewsController < ActionController::Base
|
|
5
|
+
def show
|
|
6
|
+
@libraries = preview.libraries
|
|
7
|
+
@library = params[:library] || RailsIcons.configuration.default_library || @libraries.first
|
|
8
|
+
@default_library = RailsIcons.configuration.default_library
|
|
9
|
+
@default_variant = library_default_variant
|
|
10
|
+
@variant = params[:variant] || @default_variant
|
|
11
|
+
@variants = preview.variants(@library)
|
|
12
|
+
|
|
13
|
+
@icon_names = preview.icon_names(@library, @variant)
|
|
14
|
+
@tags = preview.tags(@library, @variant)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def preview
|
|
20
|
+
@preview ||= Preview.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def library_default_variant
|
|
24
|
+
RailsIcons.configuration.libraries[@library.to_sym]&.default_variant ||
|
|
25
|
+
RailsIcons.configuration.default_variant
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|