Skip to content

Commit

Permalink
Merge pull request storybookjs#15545 from storybookjs/bring-mesure-ad…
Browse files Browse the repository at this point in the history
…don-to-monorepo

Essentials: Add measure addon to monorepo
  • Loading branch information
shilman committed Jul 10, 2021
2 parents 1dccc27 + c165b36 commit f6c296c
Show file tree
Hide file tree
Showing 39 changed files with 1,572 additions and 46 deletions.
2 changes: 1 addition & 1 deletion addons/essentials/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@storybook/addon-backgrounds": "6.4.0-alpha.13",
"@storybook/addon-controls": "6.4.0-alpha.13",
"@storybook/addon-docs": "6.4.0-alpha.13",
"@storybook/addon-measure": "^2.0.0",
"@storybook/addon-measure": "6.4.0-alpha.13",
"@storybook/addon-outline": "6.4.0-alpha.13",
"@storybook/addon-toolbars": "6.4.0-alpha.13",
"@storybook/addon-viewport": "6.4.0-alpha.13",
Expand Down
33 changes: 33 additions & 0 deletions addons/measure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Storybook Addon Measure

Storybook addon for inspecting layouts and visualizing the box model.

1. Press the <kbd>m</kbd> key to enable the addon:

2. Hover over a DOM node

3. Storybook will display the dimensions of the selected element—margin, padding, border, width and height—in pixels.

![](https://user-images.githubusercontent.com/42671/119589961-dff9b380-bda1-11eb-9550-7ae28bc70bf4.gif)

## Usage

This addon requires Storybook 6.3 or later. Measure is part of [essentials](https://storybook.js.org/docs/react/essentials/introduction) and so is installed in all new Storybooks by default. If you need to add it to your Storybook, you can run:

```sh
npm i -D @storybook/addon-measure
```

Add `"@storybook/addon-measure"` to the addons array in your `.storybook/main.js`:

```js
module.exports = {
addons: ['@storybook/addon-measure'],
};
```

### Inspiration

- [Inspx](https://github.com/raunofreiberg/inspx) by Rauno Freiberg
- [Aaron Westbrook's script](https://gist.github.com/awestbro/e668c12662ad354f02a413205b65fce7)
- [Visbug](https://visbug.web.app/) from the Chrome team
82 changes: 82 additions & 0 deletions addons/measure/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"name": "@storybook/addon-measure",
"version": "6.4.0-alpha.13",
"description": "Inspect layouts by visualizing the box model",
"keywords": [
"storybook-addons",
"essentials",
"style",
"CSS",
"design"
],
"homepage": "https://github.com/storybookjs/storybook/tree/main/addons/measure",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "addons/measure"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"author": "winkerVSbecks",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/ts3.9/index.d.ts",
"typesVersions": {
"<3.8": {
"*": [
"dist/ts3.4/*"
]
}
},
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.4.0-alpha.13",
"@storybook/api": "6.4.0-alpha.13",
"@storybook/client-logger": "6.4.0-alpha.13",
"@storybook/components": "6.4.0-alpha.13",
"@storybook/core-events": "6.4.0-alpha.13",
"core-js": "^3.8.2",
"global": "^4.4.0"
},
"devDependencies": {
"@types/webpack-env": "^1.16.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},
"gitHead": "70d04492b677c52c518b1b9591e382ba57484042",
"sbmodern": "dist/modern/index.js",
"storybook": {
"displayName": "Measure",
"unsupportedFrameworks": [
"react-native"
],
"icon": "https://user-images.githubusercontent.com/42671/119589951-dbcd9600-bda1-11eb-9227-078f3cfc1e74.png"
}
}
12 changes: 12 additions & 0 deletions addons/measure/preset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function config(entry = []) {
return [...entry, require.resolve('./dist/esm/preset/addDecorator')];
}

function managerEntries(entry = [], options) {
return [...entry, require.resolve('./dist/esm/register')];
}

module.exports = {
managerEntries,
config,
};
1 change: 1 addition & 0 deletions addons/measure/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('./dist/esm/register');
39 changes: 39 additions & 0 deletions addons/measure/src/Tool.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useCallback, useEffect } from 'react';
import { useGlobals, useStorybookApi } from '@storybook/api';
import { Icons, IconButton } from '@storybook/components';
import { TOOL_ID, ADDON_ID } from './constants';

export const Tool = () => {
const [globals, updateGlobals] = useGlobals();
const { measureEnabled } = globals;
const api = useStorybookApi();

const toggleMeasure = useCallback(
() =>
updateGlobals({
measureEnabled: !measureEnabled,
}),
[updateGlobals, measureEnabled]
);

useEffect(() => {
api.setAddonShortcut(ADDON_ID, {
label: 'Toggle Measure [M]',
defaultShortcut: ['M'],
actionName: 'measure',
showInMenu: false,
action: toggleMeasure,
});
}, [toggleMeasure, api]);

return (
<IconButton
key={TOOL_ID}
active={measureEnabled}
title="Enable measure"
onClick={toggleMeasure}
>
<Icons icon="ruler" />
</IconButton>
);
};
97 changes: 97 additions & 0 deletions addons/measure/src/box-model/canvas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* eslint-disable no-param-reassign */
import global from 'global';

interface Size {
width: number;
height: number;
}

interface CanvasState {
canvas?: HTMLCanvasElement;
context?: CanvasRenderingContext2D;
width?: number;
height?: number;
}

function getDocumentWidthAndHeight() {
const container = global.document.documentElement;

const height = Math.max(container.scrollHeight, container.offsetHeight);
const width = Math.max(container.scrollWidth, container.offsetWidth);
return { width, height };
}

function createCanvas(): CanvasState {
const canvas = global.document.createElement('canvas');
canvas.id = 'storybook-addon-measure';
const context = canvas.getContext('2d');
// Set canvas width & height
const { width, height } = getDocumentWidthAndHeight();
setCanvasWidthAndHeight(canvas, context, { width, height });
// Position canvas
canvas.style.position = 'absolute';
canvas.style.left = '0';
canvas.style.top = '0';
canvas.style.zIndex = '100000';
// Disable any user interactions
canvas.style.pointerEvents = 'none';
global.document.body.appendChild(canvas);

return { canvas, context, width, height };
}

function setCanvasWidthAndHeight(
canvas: HTMLCanvasElement,
context: CanvasRenderingContext2D,
{ width, height }: Size
) {
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;

// Scale
const scale = global.window.devicePixelRatio;
canvas.width = Math.floor(width * scale);
canvas.height = Math.floor(height * scale);

// Normalize coordinate system to use css pixels.
context.scale(scale, scale);
}

let state: CanvasState = {};

export function init() {
if (!state.canvas) {
state = createCanvas();
}
}

export function clear() {
if (state.context) {
state.context.clearRect(0, 0, state.width, state.height);
}
}

export function draw(callback: (context: CanvasRenderingContext2D) => void) {
clear();
callback(state.context);
}

export function rescale() {
// First reset so that the canvas size doesn't impact the container size
setCanvasWidthAndHeight(state.canvas, state.context, { width: 0, height: 0 });

const { width, height } = getDocumentWidthAndHeight();
setCanvasWidthAndHeight(state.canvas, state.context, { width, height });

// update state
state.width = width;
state.height = height;
}

export function destroy() {
if (state.canvas) {
clear();
state.canvas.parentNode.removeChild(state.canvas);
state = {};
}
}
Loading

0 comments on commit f6c296c

Please sign in to comment.