Build less #import url dynamically - import

While working on a google webfont import mixin, I noticed that it is not possible to build #import URLs dynamically.
.gFontImport (#name, #weights, #subsets) {
#url: "http://fonts.googleapis.com/css?family=#{name}:#{weights}&subset=#{subsets}";
#import url(#url);
}
Which can be narrowed down to
#url: "http://localhost/test.css";
#import url(#url);
Neither any of these additional tests work:
#import url("#{url}"); // this one renders at least
#import url(~"#{url}");
When the compiler renders the CSS file the #import URL is always left unchanged, e.g. "#{url}"

It seems to work in 1.4 beta.
1.4
Doing something like this (I tried it out at less2css.org) in LESS:
.gFontImport (#name, #weights, #subsets) {
#import url('http://fonts.googleapis.com/css?family=#{name}:#{weights}&subset=#{subsets}');
}
.gFontImport("Roboto+Slab",400,latin);
will have the expected output in CSS:
#import url('http://fonts.googleapis.com/css?family=Roboto+Slab:400&subset=latin');
<=1.3.3
If it does not work for you, you can use a workaround that injects the #import call into a selector name:
.gFontImport (#name, #weights, #subsets) {
#gimport: ~"#import url('http://fonts.googleapis.com/css?family=#{name}:#{weights}&subset=#{subsets}');";
#{gimport} some-selector {/* */};
}
so calling something like this in LESS:
.gFontImport("Roboto+Slab",400,latin);
will output this in CSS:
#import url('http://fonts.googleapis.com/css?family=Roboto+Slab:400&subset=latin');
some-selector {
/**/
}
This works, but it is a tiny bit messy. In a javascript implementation of LESS you can use
`'\n'`
and
`'\t'`
in the string interpolation to make it look a bit tidier.

I've made a gist with working mixins for both versions
https://gist.github.com/line-o/5568723

A working example, that defines font sets :
(with Less v1.7.3)
// Google font import mixin
.gFontImport (#name; #weights: 400; #subsets: latin) {
#nameEsc: ~`"#{name}".replace(/'([^']+)'/,'$1').replace(' ', '+')`;
#import url('http://fonts.googleapis.com/css?family=#{nameEsc}:#{weights}&subset=#{subsets}');
}
// Font sets
// The variable #post-fontset is dynamic (user input)
// ------------------
// foo1
.font(#fontset) when (#fontset = foo1){
.gFontImport('Roboto');
.gFontImport('Lato');
}
.font-text() when (#post-fontset = foo1){
font-family:Lato;
}
.font-headings() when (#post-fontset = foo1){
font-family:Lato;
}
.font-sitetitle() when (#post-fontset = foo1){
font-family:Roboto;
}
// foo2
.font(#fontset) when (#fontset = foo2){
.gFontImport('Oswald');
.gFontImport('Lato');
.gFontImport('Roboto Slab');
}
.font-text() when (#post-fontset = foo2){
font-family:'Roboto Slab';
}
.font-headings() when (#post-fontset = foo2){
font-family:Lato;
}
.font-sitetitle() when (#post-fontset = foo2){
font-family:Oswald;
}
// Using the mixins in the CSS
// ------------------
// This executes the font import mixin
.font(#post-fontset);
// Site title
h1{.font-sitetitle();}
// Heading
h2, h3, h4, h5, h6{.font-headings();}
// Text
body{.font-text();}

Related

Prevent Sass framework variable collisions

Is there a way that I could import multiple framework Sass files in a way that each framework will use its own variables without the variables of one framework overriding/colliding with the variables of the other while I would still be able to access the variables of one or both of those frameworks in subsequent styles as needed?
For example:
framework-a-variables.scss:
$border-color: green !default;
framework-a-box.scss:
.box-a { border: 1px solid $border-color; }
framework-b-variables.scss:
$border-color: red !default;
framework-b-box.scss:
.box-b { border: 1px solid $border-color; }
app-styles.scss:
#import 'framework-a-variables';
#import 'framework-a-box';
#import 'framework-b-variables';
#import 'framework-b-box';
This will result in an element with a box-b class having a green border due to how Sass variable declarations and the !default keyword work. Likewise, if I wanted to use $border-color as defined by framework-b-variables.scss in, say, button-styles.scss, I wouldn't be able to.
I have tried using placeholder selectors to scope the Sass imports so that the variables initialized by each framework are scoped to be used only by the other imported files of that framework. This seems to scope the variables as desired, but none of the styles are compiled unless I extend the placeholder selector, but Sass requires that extend directives be used only within rules and I wouldn't want to increase the specificity of all my selectors just to achieve this.
%framework-a-scope {
#import 'framework-a-variables';
#import 'framework-a-box';
}
%framework-b-scope {
#import 'framework-b-variables';
#import 'framework-b-box';
}
#extend %framework-a-scope; // This is not allowed in Sass
#extend %framework-a-scope; // This is not allowed in Sass
html {
#extend %framework-a-scope; // Wouldn't want do this
#extend %framework-b-scope; // Wouldn't want do this
}
I have also tried using mixins to scope the Sass imports. I believe that mixins, unlike extends, can output rules without being nested within rules, but Sass does not allow import directives within control directives or mixins.
#mixin framework-a-scope() {
#import 'framework-a-variables'; // This is not allowed in Sass
#import 'framework-a-box'; // This is not allowed in Sass
}
#include framework-a-scope();
So, is there any way to achieve what I am looking to achieve? I don't mind having to explicitly import variables in specific scopes when needed, for example:
.my-custom-box-1 {
#import 'framework-a-variables';
border: 1px solid $border-color;
}
.my-custom-box-2 {
#import 'framework-b-variables';
border: 1px solid $border-color;
}

Create an instance of a React class from a string

I have a string which contains a name of the Class (this is coming from a json file). This string tells my Template Class which layout / template to use for the data (also in json). The issue is my layout is not displaying.
Home.jsx:
//a template or layout.
var Home = React.createClass({
render () {
return (
<div>Home layout</div>
)
}
});
Template.jsx:
var Template = React.createClass({
render: function() {
var Tag = this.props.template; //this is the name of the class eg. 'Home'
return (
<Tag />
);
}
});
I don't get any errors but I also don't see the layout / Home Class. I've checked the props.template and this logs the correct info. Also, I can see the home element in the DOM. However it looks like this:
<div id='template-holder>
<home></home>
</div>
If I change following line to:
var Tag = Home;
//this works but it's not dynamic!
Any ideas, how I can fix this? I'm sure it's either simple fix or I'm doing something stupid. Help would be appreciated. Apologies if this has already been asked (I couldn't find it).
Thanks,
Ewan
This will not work:
var Home = React.createClass({ ... });
var Component = "Home";
React.render(<Component />, ...);
However, this will:
var Home = React.createClass({ ... });
var Component = Home;
React.render(<Component />, ...);
So you simply need to find a way to map between the string "Home" and the component class Home. A simple object will work as a basic registry, and you can build from there if you need more features.
var components = {
"Home": Home,
"Other": OtherComponent
};
var Component = components[this.props.template];
No need to manually map your classes to a dictionary, or "registry", as in Michelle's answer. A wildcard import statement is already a dictionary!
import * as widgets from 'widgets';
const Type = widgets[this.props.template];
...
<Type />
You can make it work with multiple modules by merging all the dictionaries into one:
import * as widgets from 'widgets';
import * as widgets2 from 'widgets2';
const registry = Object.assign({}, widgets, widgets2);
const widget = registry[this.props.template];
I would totally do this to get dynamic dispatch of react components. In fact I think I am in a bunch of projects.
I had the same problem, and found out the solution by myself. I don't know if is the "best pratice" but it works and I'm using it currently in my solution.
You can simply make use of the "evil" eval function to dynamically create an instance of a react component. Something like:
function createComponent(componentName, props, children){
var component = React.createElement(eval(componentName), props, children);
return component;
}
Then, just call it where you want:
var homeComponent = createComponent('Home', [props], [...children]);
If it fits your needs, maybe you can consider something like this.
Hope it helps.
I wanted to know how to create React classes dynamically from a JSON spec loaded from a database and so I did some experimenting and figured it out. My basic idea was that I wanted to define a React app through a GUI instead of typing in code in a text editor.
This is compatible with React 16.3.2. Note React.createClass has been moved into its own module.
Here's condensed version of the essential parts:
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import createReactClass from 'create-react-class'
const spec = {
// getDefaultProps
// getInitialState
// propTypes: { ... }
render () {
return React.createElement('div', null, 'Some text to render')
}
}
const component = createReactClass(spec)
const factory = React.createFactory(component)
const instance = factory({ /* props */ })
const str = ReactDOMServer.renderToStaticMarkup(instance)
console.log(str)
You can see a more complete example here:
https://github.com/brennancheung/02-dynamic-react/blob/master/src/commands/tests/createClass.test.js
Here is the way it will work from a string content without embedding your components as statically linked code into your package, as others have suggested.
import React from 'react';
import { Button } from 'semantic-ui-react';
import createReactClass from 'create-react-class';
export default class Demo extends React.Component {
render() {
const s = "return { render() { return rce('div', null, rce(components['Button'], {content: this.props.propA}), rce(components['Button'], {content: 'hardcoded content'})); } }"
const createComponentSpec = new Function("rce", "components", s);
const componentSpec = createComponentSpec(React.createElement, { "Button": Button });
const component = React.createElement(createReactClass(componentSpec), { propA: "content from property" }, null);
return (
<div>
{component}
</div>
)
}
}
The React class specification is in string s. Note the following:
rce stands for React.createElement and given as a first param when callingcreateComponentSpec.
components is a dictionary of extra component types and given as a second param when callingcreateComponentSpec. This is done so that you can provide components with clashing names.
For example string Button can be resolved to standard HTML button, or button from Semantic UI.
You can easily generate content for s by using https://babeljs.io as described in https://reactjs.org/docs/react-without-jsx.html. Essentially, the string can't contain JSX stuff, and has to be plain JavaScript. That's what BabelJS is doing by translating JSX into JavaScript.
All you need to do is replace React.createElement with rce, and resolve external components via components dictionary (if you don't use external components, that you can skip the dictionary stuff).
Here is equivalent what in the code above. The same <div> with two Semantic UI Buttons in it.
JSX render() code:
function render() {
return (
<div>
<Button content={this.props.propA}/>
<Button content='hardcoded content'/>
</div>
);
}
BabelJS translates it into:
function render() {
return React.createElement("div", null, React.createElement(Button, {
content: this.props.propA
}), React.createElement(Button, {
content: "hardcoded content"
}));
}
And you do replacement as outlined above:
render() { return rce('div', null, rce(components['Button'], {content: this.props.propA}), rce(components['Button'], {content: 'hardcoded content'})); }
Calling createComponentSpec function will create a spec for React class.
Which then converted into actual React class with createReactClass.
And then brought to life with React.createElement.
All you need to do is return it from main component render func.
When you use JSX you can either render HTML tags (strings) or React components (classes).
When you do var Tag = Home, it works because the JSX compiler transforms it to:
var Template = React.createElement(Tag, {});
with the variable Tag in the same scope and being a React class.
var Tag = Home = React.createClass({
render () {
return (
<div>Home layout</div>
)
}
});
When you do
var Tag = this.props.template; // example: Tag = "aClassName"
you are doing
var Template = React.createElement("aClassName", null);
But "aClassName" is not a valid HTML tag.
Look here

How to set Stylus option to respect #import "sheet.css" directives for broccoli-stylus-single

Not sure how to set options for the Stylus CSS pre processor. A search of all the files reveals this:
node_modules/broccoli-stylus-single/node_modules/.bin/stylus [98]:
node_modules/broccoli-stylus-single/node_modules/stylus/bin/stylus [98]:
/**
* Include CSS on import.
*/
var includeCSS = false;
Does anyone know the correct/preferred way to set this option to 'true' in Ember-CLI framework / Broccoli build system?
Many thanks!
It appears that this maps to "include css": true in the options map (source):
var Compiler = module.exports = function Compiler(root, options) {
// ...
this.includeCSS = options['include css'];
// ...
};
I've tested this with ember-cli-stylus and it appears to work.

Xcode 4.5: Broken indentation when using with preprocessor directives

I'm currently using Xcode 4.5.2. There's something weird with indentation when using preprocessor directives (the conditional inclusions). Here's what my code looks like:
-(void)someMethod{
#if defined (TEST_A) || defined (TEST_B)
if (![self TESTmethod]) {
//do sth
#else
if (![self method]) {
#endif
//do sth
}
}
When I hit 'return/enter' , the cursor move right-side with some extra space as shown in picture below:
If I remove preprocessor directives it will indent correctly, but I want to use preprocessor directives. Any solution? Thanks
I have solve it by altering the code:
-(void)someMethod{
#if defined (TEST_A) || defined (TEST_B)
if (![self TESTmethod]) {
//do sth
}
#else
if (![self method]) {
//do sth
}
#endif
}
This solve the indentation problem. Weird

How to use plugin for phonegap

I am new in phonegap development . I have created one plugin class for iphone which contains some method for encryption. I want to use that .h and .m file to encrypt my data and get the data from my class file in my html page. But i don't have any idea how to call that class function in my javascript file as a result of my page. Please any one have idea how to call any class file and its method in javascript for iphone application then help me .I am waiting for reply.
See the link that Paul answered. For a quick run down, there are a couple ways to do this, but mine usually end up something like this.
Javascript:
PhoneGap.exec("NameOfObjectiveCFile.nameofMethod","parameter1", "parameter2");
Objective C:
-(void)nameOfMethod:(NSMutableArray*)paramArray withDict: (NSMutableDictionary*) options
{
//Do your stuff
//Access your params you sent in javascript by doing the following
NSString *parameter1 = [paramArray objectAtIndex:0];
//Send stuff back to Javascript using a callback function
NSString *jsCallBack = [NSString stringWithFormat:#"nameofJavascriptFinishedMethod(%#)",parameterToSendBack];
[self.webview stringByEvaluatingJavaScriptFromString:jsCallBack];
}
Back to Javascript:
function nameofJavascriptFinishedMethod(parameterFromObjectiveC)
{
//Do stuff with information from objective C
}
Also remember you need to register your plugin in your Phonegap.plist
Hope this helps, good luck.
Update: If you want your html to send information to your plugin, i'd use an html form or button or trigger an action that will call your javascript function (The first one above) and pass your variables gathered from your fields. See this link for form basics.
Update 2
1)Create new phonegap project in xcode and build to get www folder
2)Add your existing Encryption objective C files to project
3)Create new objective C class, call it EncryptPlugin (See step 5 for next)
4)Edit PhoneGap.plist file
a)add a new entry under Plugins
b)name it EncryptPlugin String EncryptPlugin
5)Header file for EncryptPlugin should look like this
#import <Foundation/Foundation.h>
#import <UIKit/UIkit.h>
#ifdef PHONEGAP_FRAMEWORK
#import <PhoneGap/PGPlugin.h>
#else
#import "PGPlugin.h"
#endif
#interface EncryptPlugin : PGPlugin {
}
-(void) useMyEncryptionFiles:(NSMutableArray*)paramArray withDict:(NSMutableDictionary*)options;
#end
6)Implementation file should look like this
#import "EncryptPlugin.h"
#import "EncryptPacket.h"
#implementation EncryptPlugin
//phonegap magic -- basically ignore
-(PGPlugin*) initWithWebView:(UIWebView*)theWebView
{
self = (PdfCreator*)[super initWithWebView:theWebView];
return self;
}
//The method that your javascript is going to call
-(void) useMyEncryptionFiles:(NSMutableArray*)paramArray withDict:(NSMutableDictionary*)options
{
//This gives you your string
NSString *stringFromJavascript = [paramArray objectAtIndex:0];
EncryptPacket *ep = [[EncryptPacket alloc] init];
NSString *encryptedString = [ep encryptRequest:stringFromJavascript];
NSLog(#"encryptedString = %#",encryptedString);
}
#end
7)In your html javascript call the function using something like this
p1.html
<head><script type="text/javascript" src="encryptdata.js"></script></head>
<body>
<input type="password" name="confirmPassword" id="confirmPassword" value="" />
<input type="button" value="FetchR" onclick="fetchRelation()"/>
</body>
encryptdata.js
function fetchRelation()
{
var getvalue=document.getElementById('confirmPassword').value;
//what is next step....... to send the data to the plugin class
PhoneGap.exec("EncryptPlugin.useMyEncrptionFiles",getValue);
}
This should get you started. If you want to send stuff back to your html javascript then use the function I specified above:
NSString *jsCallBack = [NSStringstringWithFormat:#"nameofJavascriptFinishedMethod(%#)",parameterToSendBack];
[self.webview stringByEvaluatingJavaScriptFromString:jsCallBack];
Hopefully this helps, you should be able to figure it out with these instructions. They probably aren't perfect, but I didn't have time to put it all together and compile it using your code. Good luck.
See How to Create a PhoneGap Plugin for iOS