Skip to content

Commit

Permalink
Merge branch '1.3.0'
Browse files Browse the repository at this point in the history
# Conflicts:
#	README.md
  • Loading branch information
ben-rogerson committed May 30, 2020
2 parents 38e736b + 6b528a3 commit 23fe75c
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 59 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@ Add conditional styles on jsx elements with the css import and the css prop:
```js
import tw, { css } from 'twin.macro'

const Input = ({ hasDarkHover }) => (
const Input = ({ hasHover }) => (
<input
css={[
hasDarkHover && tw`hover:border-black`,
hasHover && tw`hover:border-black`,
tw`border`,
css`
color: white;
`,
]}
/>
)
export default () => <Input hasDarkHover />
export default () => <Input hasHover />
```

## How it works
Expand Down
10 changes: 10 additions & 0 deletions __snapshots__/plugin.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,16 @@ const usageTest = {
'line-height': '1.25',
'--text-opacity': '1',
color: 'rgba(245, 101, 101, var(--text-opacity))',
'@media (min-width: 768px)': {
'font-size': '0.875rem !important',
'font-weight': '500 !important',
'line-height': '1.25 !important',
},
'@media (min-width: 1024px)': {
'font-size': '0.875rem',
'font-weight': '500',
'line-height': '1.25',
},
}
Expand Down
19 changes: 19 additions & 0 deletions docs/feature-differences.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Twin and Tailwind differences

This document outlines the feature differences between Twin and Tailwind.

## Additional features

- tw can be imported as a named import<br/>eg: `import { tw } from 'twin.macro'`<br/>You'll receive errors in a TypeScript project though
- `container` has left/right margins
- `container` has left/right paddings
- Variants can be stacked<br/>eg: `md:before:flex!`
- You can add a negative value to just about any measurement class

## Twin doesn't have

- [Class prefixes](https://tailwindcss.com/docs/configuration/#prefix)
- [Custom separators](https://tailwindcss.com/docs/configuration/#separator)
- You can't disable [core plugins](https://tailwindcss.com/docs/configuration/#core-plugins)
- Twin doesn't have an `important` option in your config<br/>Use the trailing bang feature instead, eg: `flex!`
- You can't limit the available variants on each class, all of them are always available
46 changes: 23 additions & 23 deletions docs/attrs.md → docs/group.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Using group
# Using the group className

There’s only one Tailwind class that can’t be used within `tw` prop or function and that’s the `group` class. It needs to be added as a className so variants like `group-hover:` will work correctly when added to the children.

Here are the rest of the group variants you can use with Twin:

- group-hover
- group-focus
- group-hocus (hover + focus)
- group-active
- group-visited
- `group-hover`
- `group-focus`
- `group-hocus` - A combination of hover and focus
- `group-active`
- `group-visited`

Using the group className with the `tw` prop is similar to vanilla Tailwind. Adding the group as a className on the parent allows the group variants to work as intended on the child elements:

Expand All @@ -18,7 +18,7 @@ import 'twin.macro'
export default () => (
<button className="group">
<div tw="group-hover:bg-black">Child 1</div>
<div tw="group-hover:bg-white">Child 2</div>
<div tw="group-hover:font-bold">Child 2</div>
</button>
)
```
Expand All @@ -28,22 +28,22 @@ When working in Emotion and Styled Components without the `group` classes, the e
```js
import tw, { styled } from 'twin.macro'

const Group = tw.button`group`
const Group = tw.button``
Group.Child1 = styled.div`
${Group}:hover & {
${tw`bg-black`}
}
`
Group.Child2 = styled.div`
${Group}:hover & {
${tw`bg-white`}
${tw`font-bold`}
}
`

export default () => (
<Group>
<Group.Child1 />
<Group.Child2 />
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
```
Expand All @@ -69,12 +69,12 @@ import tw, { styled } from 'twin.macro'
const Group = styled.button.attrs({ className: 'group' })``

Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:bg-white`
Group.Child2 = tw.div`group-hover:font-bold`

export default () => (
<Group>
<Group.Child1 />
<Group.Child2 />
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
```
Expand All @@ -88,12 +88,12 @@ import tw from 'twin.macro'

const Group = tw.button``
Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:bg-white`
Group.Child2 = tw.div`group-hover:font-bold`

export default () => (
<Group className="group">
<Group.Child1 />
<Group.Child2 />
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
```
Expand All @@ -111,12 +111,12 @@ const Button = tw.button``
const Group = withAttrs(Button, { className: 'group' })

Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:bg-white`
Group.Child2 = tw.div`group-hover:font-bold`

export default () => (
<Group>
<Group.Child1 />
<Group.Child2 />
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
```
Expand All @@ -130,12 +130,12 @@ const Group = tw.button``
Group.defaultProps = { className: 'group' }

Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:bg-white`
Group.Child2 = tw.div`group-hover:font-bold`

export default () => (
<Group>
<Group.Child1 />
<Group.Child2 />
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
```
Expand Down
41 changes: 26 additions & 15 deletions src/getStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export default (classes, t, state) => {

// Move and sort the responsive items to the end of the list
const classesOrdered = orderByScreens(classes, state)

const theme = getTheme(state.config.theme)
const hasUserPlugins = !isEmpty(state.config.plugins)

// Merge styles into a single css object
const styles = classesOrdered.reduce((results, classNameRaw) => {
Expand All @@ -33,28 +33,25 @@ export default (classes, t, state) => {
const pieces = getPieces({ classNameRaw, state })
const { className } = pieces

// Process addUtilities from plugins
if (hasUserPlugins) {
const style = handleUserPlugins({ config: state.config, className })
if (!isEmpty(style)) {
state.debug && debug(className, style)
return deepMerge(results, style)
}
}

const classProperties = getProperties(className)
const {
hasNoMatches,
hasUserPlugins,
dynamicKey,
dynamicConfig,
corePlugin,
type,
} = getProperties(className, state)

// Kick off suggestions when no class matches
assert(!classProperties || classProperties.hasNoMatches, () =>
assert(hasNoMatches && !hasUserPlugins, () =>
errorSuggestions({ pieces, state })
)

const { dynamicKey, dynamicConfig, corePlugin, type } = classProperties

const styleHandler = {
static: () => handleStatic({ pieces }),
dynamic: () =>
handleDynamic({ theme, pieces, state, dynamicKey, dynamicConfig }),
userPlugin: () => handleUserPlugins({ config: state.config, className }),
corePlugin: () =>
handleCorePlugins({
theme,
Expand All @@ -66,7 +63,21 @@ export default (classes, t, state) => {
}),
}

const style = applyTransforms({ type, pieces, style: styleHandler[type]() })
let style

if (hasUserPlugins) {
style = applyTransforms({
type,
pieces,
style: styleHandler.userPlugin(),
})
}

// Check again there are no userPlugin matches
assert(hasNoMatches && !style, () => errorSuggestions({ pieces, state }))

style =
style || applyTransforms({ type, pieces, style: styleHandler[type]() })

const result = deepMerge(
results,
Expand Down
13 changes: 5 additions & 8 deletions src/macro.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ const twinMacro = ({ babel: { types: t }, references, state, config }) => {
state.configExists = configExists
state.config = tailwindConfig
state.hasSuggestions =
typeof config.hasSuggestions === 'undefined' ? true : config.hasSuggestions
typeof config.hasSuggestions === 'undefined'
? true
: Boolean(config.hasSuggestions)

state.debug = config.debug || false
state.debugProp = Boolean(config.debugProp)
state.debug = Boolean(config.debug)

state.tailwindConfigIdentifier = program.scope.generateUidIdentifier(
'tailwindConfig'
Expand All @@ -44,12 +47,6 @@ const twinMacro = ({ babel: { types: t }, references, state, config }) => {
state.isDev = isDev
state.isProd = !isDev

// Dev mode coming soon
if (isDev) {
state.isDev = false
state.isProd = true
}

// Styled import
const styledImport = getStyledConfig(config)
state.styledIdentifier = findIdentifier({
Expand Down
15 changes: 15 additions & 0 deletions src/macro/debug.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const addDebugProperty = ({ t, attributes, rawClasses, path, state }) => {
if (state.isProd || !state.debugProp) return

// Remove the existing debug attribute if you happen to have it
const debugProperty = attributes.filter(
p => p.node.name && p.node.name.name === 'data-tw'
)
debugProperty.forEach(path => path.remove())
// Add the attribute
path.insertAfter(
t.jsxAttribute(t.jsxIdentifier('data-tw'), t.stringLiteral(rawClasses))
)
}

export default addDebugProperty
18 changes: 9 additions & 9 deletions src/macro/tw.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { parseTte, replaceWithLocation } from './../macroHelpers'
import { assert } from './../utils'
import { logGeneralError } from './../logging'
import addDebugProperty from './debug'

const handleTwProperty = ({ getStyles, program, t, state }) =>
program.traverse({
JSXAttribute(path) {
if (path.node.name.name !== 'tw') return
if (!state.hasTwProp) {
state.hasTwProp = true
}
state.hasTwProp = true

const nodeValue = path.node.value

Expand All @@ -25,11 +24,8 @@ const handleTwProperty = ({ getStyles, program, t, state }) =>
)
)

const styles = getStyles(
expressionValue || nodeValue.value || '',
t,
state
)
const rawClasses = expressionValue || nodeValue.value || ''
const styles = getStyles(rawClasses, t, state)

const attributes = path
.findParent(p => p.isJSXOpeningElement())
Expand All @@ -54,6 +50,8 @@ const handleTwProperty = ({ getStyles, program, t, state }) =>
)
)
}

addDebugProperty({ t, attributes, rawClasses, path, state })
},
})

Expand All @@ -71,7 +69,9 @@ const handleTwFunction = ({ getStyles, references, state, t }) => {
})
if (!parsed) return

replaceWithLocation(parsed.path, getStyles(parsed.string, t, state))
const rawClasses = parsed.string

replaceWithLocation(parsed.path, getStyles(rawClasses, t, state))
})
}

Expand Down
10 changes: 9 additions & 1 deletion src/utils/getProperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,20 @@ const getDynamicProperties = className => {
return { isDynamicClass, dynamicConfig, dynamicKey }
}

const getProperties = className => {
const isEmpty = value =>
value === undefined ||
value === null ||
(typeof value === 'object' && Object.keys(value).length === 0) ||
(typeof value === 'string' && value.trim().length === 0)

const getProperties = (className, state) => {
if (!className) return
const isStatic = isStaticClass(className)
const { isDynamicClass, dynamicConfig, dynamicKey } = getDynamicProperties(
className
)
const corePlugin = dynamicConfig.plugin
const hasUserPlugins = !isEmpty(state.config.plugins)

const type =
(isStatic && 'static') ||
Expand All @@ -53,6 +60,7 @@ const getProperties = className => {
hasNoMatches: !type,
dynamicKey,
dynamicConfig,
hasUserPlugins,
}
}

Expand Down

0 comments on commit 23fe75c

Please sign in to comment.