UNPKG

material.font.features

Version:

Material UI component for displaying opentype font features.

344 lines (343 loc) 19.4 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SettingsFormat = void 0; var jsx_runtime_1 = require("react/jsx-runtime"); var react_1 = __importDefault(require("react")); var react_autobind_1 = __importDefault(require("react-autobind")); var memoize_one_1 = __importDefault(require("memoize-one")); var opentype_features_js_1 = __importStar(require("opentype.features.js")); var Font_js_1 = __importDefault(require("opentype.features.js/lib/Font.js")); var Select_1 = __importDefault(require("@mui/material/Select")); var MenuItem_1 = __importDefault(require("@mui/material/MenuItem")); var FormControlLabel_1 = __importDefault(require("@mui/material/FormControlLabel")); var Checkbox_1 = __importDefault(require("@mui/material/Checkbox")); var InputLabel_1 = __importDefault(require("@mui/material/InputLabel")); var AlternateStylePicker_1 = __importDefault(require("./AlternateStylePicker")); var SettingsFormat; (function (SettingsFormat) { SettingsFormat["CSS"] = "css"; SettingsFormat["LEGACY"] = "legacy"; })(SettingsFormat = exports.SettingsFormat || (exports.SettingsFormat = {})); var FontFeatures = /** @class */ (function (_super) { __extends(FontFeatures, _super); function FontFeatures(props) { var _this = _super.call(this, props) || this; _this.settingsToArray = (0, memoize_one_1.default)(_this._settingsToArray); (0, react_autobind_1.default)(_this); _this.state = { features: [], fontFamily: '', }; return _this; } FontFeatures.prototype.componentDidMount = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.setFeatures()]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; FontFeatures.prototype.componentDidUpdate = function (prevProps) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(this.props.fontFile !== prevProps.fontFile || this.props.fontUrl !== prevProps.fontUrl || this.props.fontData !== prevProps.fontData)) return [3 /*break*/, 2]; return [4 /*yield*/, this.setFeatures()]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/]; } }); }); }; FontFeatures.prototype.setFeatures = function () { return __awaiter(this, void 0, void 0, function () { var features, fontFamily; return __generator(this, function (_a) { switch (_a.label) { case 0: features = []; if (!this.props.fontFile) return [3 /*break*/, 2]; return [4 /*yield*/, opentype_features_js_1.default.getFeaturesFromFile(this.props.fontFile)]; case 1: features = _a.sent(); return [3 /*break*/, 7]; case 2: if (!this.props.fontUrl) return [3 /*break*/, 4]; return [4 /*yield*/, opentype_features_js_1.default.getFeaturesFromUrl(this.props.fontUrl)]; case 3: features = (_a.sent()) || []; return [3 /*break*/, 7]; case 4: if (!this.props.fontData) return [3 /*break*/, 6]; return [4 /*yield*/, opentype_features_js_1.default.getFeaturesFromArrayBuffer(this.props.fontData)]; case 5: features = _a.sent(); return [3 /*break*/, 7]; case 6: if (this.props.fontFeatures) features = __spreadArray([], this.props.fontFeatures, true); _a.label = 7; case 7: fontFamily = this.props.fontFamily; if (!!fontFamily) return [3 /*break*/, 12]; if (!this.props.fontFile) return [3 /*break*/, 9]; return [4 /*yield*/, Font_js_1.default.getMetaDataFromFile(this.props.fontFile)]; case 8: fontFamily = (_a.sent()).fontFamily; return [3 /*break*/, 12]; case 9: if (!this.props.fontUrl) return [3 /*break*/, 11]; return [4 /*yield*/, Font_js_1.default.getMetaDataFromUrl(this.props.fontUrl)]; case 10: fontFamily = (_a.sent()).fontFamily; return [3 /*break*/, 12]; case 11: fontFamily = ''; _a.label = 12; case 12: this.setState({ features: features, fontFamily: fontFamily }); return [2 /*return*/]; } }); }); }; // public helper function that converts from old style font feature definitions to the format needed for // css property "font-feature-settings". FontFeatures.legacySettingsToCss = function (settings) { var _a; if (!settings || ((_a = settings.trim()) === null || _a === void 0 ? void 0 : _a.length) <= 0) return ''; var features = settings.split(','); var array = features.map(function (feature) { var parts = feature === null || feature === void 0 ? void 0 : feature.trim().split('='); if (!parts || parts.length < 2) return parts; return "\"".concat(parts[0].trim(), "\" ").concat(FontFeatures._numberToValue(parts[1].trim())); }); return array.filter(function (x) { return x; }).join(","); }; FontFeatures._numberToValue = function (number) { if (number === '0') return 'off'; if (number === '1') return 'on'; return number; }; FontFeatures._valueToNumber = function (value) { if (value === 'off') return '0'; if (value === 'on') return '1'; return value; }; FontFeatures.prototype.settings = function () { if (this.props.settingsFormat !== SettingsFormat.LEGACY) return this.props.settings; return FontFeatures.legacySettingsToCss(this.props.settings); }; FontFeatures.prototype.parseFeatureTagValue = function (featureSettingString) { var start = featureSettingString.indexOf('"'); var end = featureSettingString.lastIndexOf('"'); if (start === -1 || start === end) return null; var keyPart = featureSettingString.substring(start + 1, end); var valuePart = featureSettingString.substring(end + 1); keyPart = keyPart.trim(); valuePart = valuePart.trim(); if (valuePart.length <= 0) valuePart = 'on'; return { feature: keyPart, value: valuePart }; }; FontFeatures.prototype._settingsToArray = function (settings) { var _this = this; if (!settings) return []; if (settings.trim().length <= 0) return []; if (settings.trim() === 'normal') return []; var parts = settings.split(','); parts = parts.map(function (x) { return x.trim(); }); var featureSettingsArray = parts.map(function (x) { return _this.parseFeatureTagValue(x); }).filter(function (x) { return x; }); // Only return (last) unique features keys return featureSettingsArray.reverse().filter(function (x, pos, array) { return array.findIndex(function (y) { return (y === null || y === void 0 ? void 0 : y.feature) === (x === null || x === void 0 ? void 0 : x.feature); }) === pos; }).reverse(); }; FontFeatures.prototype.settingsArrayToString = function (settings, format) { if (format === void 0) { format = SettingsFormat.CSS; } if (format !== SettingsFormat.LEGACY) return settings.map(function (x) { return "\"".concat(x.feature, "\" ").concat(x.value); }).join(', '); return settings.map(function (x) { return "".concat(x.feature, "=").concat(FontFeatures._valueToNumber(x.value)); }).join(','); }; FontFeatures.prototype.handleCheckBoxChange = function (e, tag) { var _a; var enabled = (_a = e.target) === null || _a === void 0 ? void 0 : _a.checked; var updatedValue = { feature: tag, value: enabled ? 'on' : 'off' }; this.emitSettingsChanged(updatedValue); }; FontFeatures.prototype.emitSettingsChanged = function (updatedValue) { var settingsArray = this.settingsToArray(this.settings()) || []; var existingSettingIndex = settingsArray.findIndex(function (x) { return (x === null || x === void 0 ? void 0 : x.feature) === updatedValue.feature; }); if (existingSettingIndex !== -1) settingsArray[existingSettingIndex] = updatedValue; else settingsArray.push(updatedValue); var newSettingString = this.settingsArrayToString(settingsArray, this.props.settingsFormat); this.props.onSettingsChanged(newSettingString); }; FontFeatures.prototype.isCheckBoxEnabled = function (tag) { var settingsArray = this.settingsToArray(this.settings()); var index = settingsArray.findIndex(function (x) { return (x === null || x === void 0 ? void 0 : x.feature) === tag; }); if (index === -1) return false; return !(settingsArray[index].value === 'off' || settingsArray[index].value === '0'); }; FontFeatures.prototype.getCurrentSelectValue = function (tag, options) { var settingsArray = this.settingsToArray(this.settings()); var index = settingsArray.findIndex(function (x) { return (x === null || x === void 0 ? void 0 : x.feature) === tag; }); if (index === -1) return 0; var optionIndex = parseInt(FontFeatures._valueToNumber(settingsArray[index].value)); if (optionIndex >= options.length) return 0; return optionIndex; }; FontFeatures.prototype.handleComboBoxChange = function (e, tag) { var newValue = e.target.value; var updatedValue = { feature: tag, value: newValue.toString() }; this.emitSettingsChanged(updatedValue); }; FontFeatures.prototype.handleStylePickerChange = function (value, tag) { var updatedValue = { feature: tag, value: value.toString() }; this.emitSettingsChanged(updatedValue); }; FontFeatures.prototype.render = function () { var _this = this; return (0, jsx_runtime_1.jsx)("div", __assign({ className: 'font-features-container' }, { children: this.state.features.map(function (f) { var _a, _b, _c; if (!_this.props.showHidden && f.type && (f.type & opentype_features_js_1.FeatureType.Hidden)) return undefined; var control = null; if (((_a = f.alternateCharacters) === null || _a === void 0 ? void 0 : _a.length) > 0 && f.options.length > 1) control = (0, jsx_runtime_1.jsx)(AlternateStylePicker_1.default, { fontFamily: _this.state.fontFamily, description: f.description, character: f.alternateCharacters[0], tag: f.tag, options: f.options, value: _this.getCurrentSelectValue(f.tag, f.options), onChange: function (value) { return _this.handleStylePickerChange(value === null || value === void 0 ? void 0 : value.toString(), f.tag); } }); else if (f.options.length > 1) control = (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(InputLabel_1.default, { children: f.description }), (0, jsx_runtime_1.jsx)(Select_1.default, __assign({ label: f.description, value: _this.getCurrentSelectValue(f.tag, f.options), onChange: function (e) { return _this.handleComboBoxChange(e, f.tag); } }, { children: f.options.map(function (option, index) { return (0, jsx_runtime_1.jsx)(MenuItem_1.default, __assign({ value: index }, { children: option }), f.tag + '_' + "index"); }) }))] }); else if (((_b = f.alternateCharacters) === null || _b === void 0 ? void 0 : _b.length) > 0) control = (0, jsx_runtime_1.jsx)(AlternateStylePicker_1.default, { fontFamily: _this.state.fontFamily, description: f.description, character: f.alternateCharacters[0], tag: f.tag, options: ['off', 'on'], value: _this.isCheckBoxEnabled(f.tag) ? 1 : 0, onChange: function (value) { return _this.handleStylePickerChange(value === null || value === void 0 ? void 0 : value.toString(), f.tag); } }); else control = (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { checked: _this.isCheckBoxEnabled(f.tag), onChange: function (e) { return _this.handleCheckBoxChange(e, f.tag); } }), label: f.description || ((_c = f.options) === null || _c === void 0 ? void 0 : _c[0]) || f.tag }); return (0, jsx_runtime_1.jsx)("div", __assign({ className: 'font-features-entry' }, { children: control }), f.tag); }) })); }; FontFeatures.defaultProps = { settingsFormat: SettingsFormat.CSS }; return FontFeatures; }(react_1.default.PureComponent)); exports.default = FontFeatures;