1+ import { LitElement , html , css } from 'beaker://app-stdlib/vendor/lit-element/lit-element.js'
2+ import { repeat } from 'beaker://app-stdlib/vendor/lit-element/lit-html/directives/repeat.js'
3+
4+ class DriveView extends LitElement {
5+ static get styles ( ) {
6+ return css `
7+ :host {
8+ --sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Ubuntu, Cantarell, "Oxygen Sans", "Helvetica Neue", sans-serif;
9+ --monospace: Consolas, 'Lucida Console', Monaco, monospace;
10+
11+ display: block;
12+ max-width: 800px;
13+ margin: 0 auto;
14+ padding: 0 30px;
15+ min-height: calc(100vh - 20px);
16+ font-family: var(--sans-serif);
17+ }
18+
19+ a {
20+ text-decoration: none;
21+ color: #2864dc;
22+ }
23+
24+ a:hover {
25+ text-decoration: underline;
26+ }
27+
28+ hr {
29+ border: 0;
30+ border-top: 1px solid #dde;
31+ }
32+
33+ header {
34+ padding: 20px 0 20px;
35+ }
36+
37+ header h1,
38+ header p {
39+ margin: 0 0 10px;
40+ line-height: 1;
41+ }
42+
43+ header h1 {
44+ letter-spacing: 0.75px;
45+ }
46+
47+ header p {
48+ font-size: 13px;
49+ letter-spacing: 0.25px;
50+ color: #556;
51+ }
52+
53+ header img {
54+ object-fit: cover;
55+ border-radius: 4px;
56+ height: 25px;
57+ width: 26px;
58+ position: relative;
59+ top: 1px;
60+ }
61+
62+ header .ctrls {
63+ float: right;
64+ font-size: 14px;
65+ }
66+
67+ main h4 {
68+ font-family: var(--monospace);
69+ margin: 0;
70+ font-size: 13px;
71+ letter-spacing: 0.75px;
72+ background: #f3f3f8;
73+ border-radius: 4px;
74+ padding: 4px 8px;
75+ }
76+
77+ .entries {
78+ display: grid;
79+ grid-template-columns: repeat(4, minmax(0, 1fr));
80+ grid-gap: 20px;
81+ margin: 20px 0;
82+ font-size: 14px;
83+ letter-spacing: 0.75px;
84+ }
85+
86+ .entries > div {
87+ white-space: nowrap;
88+ overflow: hidden;
89+ text-overflow: ellipsis;
90+ }
91+
92+ .entries .fa-fw {
93+ color: #556;
94+ }
95+
96+ .prompt {
97+ margin: 30px 0;
98+ font-size: 13px;
99+ border: 1px solid #dde;
100+ padding: 10px;
101+ border-radius: 4px;
102+ }
103+ `
104+ }
105+
106+ constructor ( ) {
107+ super ( )
108+ this . drive = new Hyperdrive ( location )
109+ this . info = undefined
110+ this . entries = [ ]
111+ this . load ( )
112+ }
113+
114+ async load ( ) {
115+ this . info = await this . drive . getInfo ( )
116+ var entries = await this . drive . readdir ( location . pathname , { includeStats : true } )
117+ entries . sort ( ( a , b ) => {
118+ return a . name . localeCompare ( b . name )
119+ } )
120+ this . directories = entries . filter ( entry => entry . stat . isDirectory ( ) )
121+ this . files = entries . filter ( entry => ! entry . stat . isDirectory ( ) )
122+ this . requestUpdate ( )
123+ }
124+
125+ render ( ) {
126+ if ( ! this . info ) return html ``
127+ return html `
128+ < link rel ="stylesheet " href ="beaker://app-stdlib/css/fontawesome.css ">
129+ < header >
130+ < div class ="ctrls ">
131+ < span class ="fas fa-fw fa-folder "> </ span >
132+ < a href ="http://hyperdrive.network/ ${ location . hostname } ${ location . pathname } " target ="_blank "> Open in files explorer</ a >
133+
134+ < span class ="fas fa-fw fa-list-alt "> </ span >
135+ < a href ="# " @click =${ this . onClickEditProperties } > Properties</ a >
136+ </ div >
137+ < h1 >
138+ < img src ="/thumb " @error =${ e => { e . currentTarget . style . display = 'none' } } >
139+ ${ this . info . title }
140+ </ h1 >
141+ < p class ="description "> ${ this . info . description || html `< em > No description</ em > ` } </ p >
142+ < p class ="type "> ${ this . info . type || 'files drive' } </ p >
143+ </ header >
144+ < main >
145+ < h4 > ${ location . pathname } </ h4 >
146+ < div class ="entries ">
147+ ${ location . pathname !== '/' ? html `
148+ < div >
149+ < span class ="fa-fw far fa-folder "> </ span >
150+ < a href =".. " title ="Go up a directory "> ..</ a >
151+ </ div >
152+ ` : '' }
153+ ${ repeat ( this . directories , entry => this . renderEntry ( entry ) ) }
154+ ${ repeat ( this . files , entry => this . renderEntry ( entry ) ) }
155+ </ div >
156+ ${ this . info . writable ? html `
157+ < div class ="prompt ">
158+ < strong > Advanced:</ strong > Create an < a @click =${ e => this . onCreateIndex ( e , 'md' ) } href ="#"> index.md</ a >
159+ or < a @click =${ e => this . onCreateIndex ( e , 'html' ) } href ="#"> index.html</ a > in this directory.
160+ </ div >
161+ ` : '' }
162+ </ main >
163+ `
164+ }
165+
166+ renderEntry ( entry ) {
167+ if ( entry . stat . mount && entry . stat . mount . key ) {
168+ return html `
169+ < div >
170+ < span class ="fa-fw fas fa-external-link-square-alt "> </ span >
171+ < a href ="hd:// ${ entry . stat . mount . key } " title =${ entry . name } >
172+ ${ entry . name }
173+ </ a >
174+ </ div >
175+ `
176+ }
177+ return html `
178+ < div >
179+ < span class ="fa-fw far fa- ${ entry . stat . isDirectory ( ) ? 'folder' : 'file-alt' } "> </ span >
180+ < a href ="./ ${ entry . name } ${ entry . stat . isDirectory ( ) ? '/' : '' } " title =${ entry . name } >
181+ ${ entry . name }
182+ </ a >
183+ </ div >
184+ `
185+ }
186+
187+ async onClickEditProperties ( e ) {
188+ e . preventDefault ( )
189+ await navigator . drivePropertiesDialog ( location . origin )
190+ this . load ( )
191+ }
192+
193+ async onCreateIndex ( e , ext ) {
194+ e . preventDefault ( )
195+ var title = location . pathname === '/' ? this . info . title : location . pathname . split ( '/' ) . filter ( Boolean ) . pop ( )
196+ if ( ext === 'md' ) {
197+ await this . drive . writeFile ( location . pathname + 'index.md' , `# ${ title } ` )
198+ } else {
199+ await this . drive . writeFile ( location . pathname + 'index.html' , `<!doctype html>
200+ <html>
201+ <head>
202+ <title>${ title } </title>
203+ </head>
204+ <body>
205+ </body>
206+ </html>` )
207+ }
208+ // TEMPORARY API please do not depend on this in your applications
209+ window . __beakerOpenEditor ( )
210+ location . reload ( )
211+ }
212+ }
213+
214+ customElements . define ( 'drive-view' , DriveView )
215+ document . body . append ( new DriveView ( ) )
0 commit comments