UNPKG

9.73 kBJavaScriptView Raw
1import { Dialog, showDialog } from '@jupyterlab/apputils';
2import * as React from 'react';
3import { VDomModel, VDomRenderer } from '@jupyterlab/apputils';
4import { TextKind, TEXT_OPTIONS, TEXT_LABELS, KIND_LABELS, PACKAGE_NAME } from '.';
5import '../style/editor.css';
6const h = React.createElement;
7const EDITOR_CLASS = 'jp-FontsEditor';
8const ENABLED_CLASS = 'jp-FontsEditor-enable';
9const FIELD_CLASS = 'jp-FontsEditor-field';
10const EMBED_CLASS = 'jp-FontsEditor-embed';
11const SECTION_CLASS = 'lm-CommandPalette-header';
12const BUTTON_CLASS = 'jp-FontsEditor-button jp-mod-styled';
13const SIZE_CLASS = 'jp-FontsEditor-size';
14const DUMMY = '-';
15export class FontEditorModel extends VDomModel {
16 get fonts() {
17 return this._fonts;
18 }
19 set fonts(fonts) {
20 if (this._fonts && this._fonts.settings) {
21 this._fonts.settings.changed.disconnect(this.onSettingsChange, this);
22 }
23 this._fonts = fonts;
24 fonts.settings.changed.connect(this.onSettingsChange, this);
25 this.stateChanged.emit(void 0);
26 }
27 onSettingsChange() {
28 this.stateChanged.emit(void 0);
29 }
30 get notebook() {
31 return this._notebook;
32 }
33 set notebook(notebook) {
34 var _a, _b;
35 if ((_a = this._notebook) === null || _a === void 0 ? void 0 : _a.model) {
36 this._notebook.model.metadata.changed.disconnect(this.onSettingsChange, this);
37 this._notebook.context.pathChanged.disconnect(this.onSettingsChange, this);
38 }
39 this._notebook = notebook;
40 if ((_b = this._notebook) === null || _b === void 0 ? void 0 : _b.model) {
41 this._notebook.model.metadata.changed.connect(this.onSettingsChange, this);
42 this._notebook.context.pathChanged.connect(this.onSettingsChange, this);
43 }
44 this.stateChanged.emit(void 0);
45 }
46 get enabled() {
47 return this._fonts.enabled;
48 }
49 async setEnabled(enabled) {
50 if (this.notebook == null) {
51 await this._fonts.settings.set('enabled', enabled);
52 this.stateChanged.emit(void 0);
53 }
54 }
55 get notebookMetadata() {
56 var _a;
57 if ((_a = this.notebook) === null || _a === void 0 ? void 0 : _a.model) {
58 return this.notebook.model.metadata.get(PACKAGE_NAME);
59 }
60 }
61 clearNotebookMetadata(fontName) {
62 var _a, _b, _c;
63 let meta = this.notebookMetadata;
64 if (fontName) {
65 if ((_a = meta) === null || _a === void 0 ? void 0 : _a.fonts) {
66 delete meta.fonts[fontName];
67 }
68 if ((_b = meta) === null || _b === void 0 ? void 0 : _b.fontLicenses) {
69 delete meta.fontLicenses[fontName];
70 }
71 }
72 if ((_c = this.notebook) === null || _c === void 0 ? void 0 : _c.model) {
73 this.notebook.model.metadata.set(PACKAGE_NAME, JSON.parse(JSON.stringify(meta)));
74 }
75 this.stateChanged.emit(void 0);
76 }
77 dispose() {
78 if (this._fonts && this._fonts.settings) {
79 this._fonts.settings.changed.disconnect(this.onSettingsChange, this);
80 }
81 super.dispose();
82 }
83}
84export class FontEditor extends VDomRenderer {
85 constructor() {
86 super(new FontEditorModel());
87 this.addClass(EDITOR_CLASS);
88 }
89 render() {
90 const m = this.model;
91 if (!m) {
92 return h('div', { key: 'empty' });
93 }
94 return h('div', { key: 'editor' }, [
95 ...this.header(),
96 ...[TextKind.code, TextKind.content].map(kind => h('section', { key: `${kind}-section`, title: KIND_LABELS[kind] }, [
97 h('h3', { key: `${kind}-header`, className: SECTION_CLASS }, KIND_LABELS[kind]),
98 ...[
99 'font-family',
100 'font-size',
101 'line-height'
102 ].map((prop) => this.textSelect(prop, kind, { key: `${kind}-${prop}` }))
103 ]))
104 ]);
105 }
106 fontFaceExtras(m, fontFamily) {
107 let font;
108 let unquoted = `${fontFamily}`.slice(1, -1);
109 if (m.fonts.fonts.get(unquoted)) {
110 font = m.fonts.fonts.get(unquoted);
111 }
112 return !font ? [] : [this.licenseButton(m, font)];
113 }
114 licenseButton(m, font) {
115 return h('button', {
116 className: BUTTON_CLASS,
117 title: font.license.name,
118 key: font.name,
119 onClick: () => m.fonts.requestLicensePane(font)
120 }, font.license.spdx);
121 }
122 textSelect(prop, kind, sectionProps) {
123 const m = this.model;
124 const onChange = (evt) => {
125 let value = evt.target.value;
126 value = value === DUMMY ? null : value;
127 m.fonts
128 .setTextStyle(prop, value, Object.assign({ kind }, (m.notebook ? { notebook: m.notebook } : {})))
129 .catch(console.warn);
130 };
131 const value = m.fonts.getTextStyle(prop, {
132 kind,
133 notebook: m.notebook || void 0
134 });
135 const extra = prop === 'font-family' ? this.fontFaceExtras(m, value) : [];
136 return h('div', Object.assign({ className: FIELD_CLASS, key: 'select-field' }, sectionProps), [
137 h('label', { key: 'select-label' }, TEXT_LABELS[prop]),
138 h('div', { key: 'select-wrap' }, [
139 ...extra,
140 h('select', {
141 className: 'jp-mod-styled',
142 title: `${TEXT_LABELS[prop]}`,
143 onChange,
144 defaultValue: value || DUMMY,
145 key: `select`
146 }, [null, ...TEXT_OPTIONS[prop](m.fonts)].map(value => {
147 return h('option', {
148 key: `'${value}'`,
149 value: value == null
150 ? DUMMY
151 : prop === 'font-family'
152 ? `'${value}'`
153 : value
154 }, value || DUMMY);
155 }))
156 ])
157 ]);
158 }
159 deleteButton(m, fontName) {
160 return h('button', {
161 className: BUTTON_CLASS,
162 title: `Delete Embedded Font`,
163 key: 'delete',
164 onClick: async () => {
165 const result = await showDialog({
166 title: `Delete Embedded Font from Notebook`,
167 body: `If you dont have ${fontName} installed, you might not be able to re-embed it`,
168 buttons: [
169 Dialog.cancelButton(),
170 Dialog.warnButton({ label: 'DELETE' })
171 ]
172 });
173 if (result.button.accept) {
174 m.clearNotebookMetadata(fontName);
175 }
176 }
177 }, 'Delete');
178 }
179 enabler(m) {
180 const onChange = async (evt) => {
181 await m.setEnabled(!!evt.currentTarget.checked);
182 };
183 return h('label', { key: 'enable-label' }, h('span', { key: 'enable-text' }, 'Enabled'), h('input', {
184 key: 'enable-input',
185 type: 'checkbox',
186 checked: m.enabled,
187 onChange
188 }));
189 }
190 embeddedFont(m, fontName) {
191 var _a;
192 if (((_a = m.notebookMetadata) === null || _a === void 0 ? void 0 : _a.fonts) == null ||
193 m.notebookMetadata.fontLicenses == null) {
194 return null;
195 }
196 const faces = m.notebookMetadata.fonts[fontName];
197 const license = m.notebookMetadata.fontLicenses[fontName];
198 const size = (faces || []).reduce((memo, face) => memo + `${face.src}`.length, license.text.length);
199 const kb = parseInt(`${size / 1024}`, 10);
200 return h('li', { key: fontName }, [
201 h('label', { key: 'label' }, fontName),
202 this.licenseButton(m, {
203 name: fontName,
204 license: {
205 name: license.name,
206 spdx: license.spdx,
207 text: async () => license.text,
208 holders: license.holders
209 },
210 faces: async () => faces || []
211 }),
212 h('span', { className: SIZE_CLASS, key: 'font-kb' }, `${kb} kb`),
213 this.deleteButton(m, fontName)
214 ]);
215 }
216 header() {
217 var _a;
218 const m = this.model;
219 const title = m.notebook
220 ? (_a = m.notebook.context.contentsModel) === null || _a === void 0 ? void 0 : _a.name.replace(/.ipynb$/, '') : 'Global';
221 this.title.label = title || 'Unknown';
222 const h2 = h('h2', { key: 'scope-head' }, [
223 h('label', { key: 'scope-label' }, `Fonts » ${title}`),
224 ...(m.notebook
225 ? [h('div', { className: 'jp-NotebookIcon', key: 'scope-icon' })]
226 : [])
227 ]);
228 if (m.notebook != null) {
229 return [
230 h2,
231 h('section', { key: 'embed-section' }, [
232 h('h3', { className: SECTION_CLASS, key: 'embed-head' }, 'Embedded fonts'),
233 h('ul', { className: EMBED_CLASS, key: 'embeds' }, Object.keys((m.notebookMetadata || {}).fonts || {}).map(fontName => {
234 return this.embeddedFont(m, fontName);
235 }))
236 ])
237 ];
238 }
239 else {
240 return [
241 h2,
242 h('section', { key: 'enable-section', className: ENABLED_CLASS }, [
243 h('h3', { className: SECTION_CLASS, key: 'enable-header' }, 'Enable/Disable All Fonts'),
244 this.enabler(m)
245 ])
246 ];
247 }
248 }
249}
250//# sourceMappingURL=editor.js.map
\No newline at end of file