NotesWhat is notes.io?

Notes brand slogan

Notes - notes.io

import React, {useState, useRef, useEffect} from 'react';
import {useHistory} from 'react-router-dom';
import ConfirmModal from '@aosprodsys/brucke-ui-core/dist/es/components/Modal';
import throttle from 'lodash-es/throttle';
import isEmpty from 'lodash-es/isEmpty';
import map from 'lodash-es/map';
import queryString from 'query-string';
import Tree from 'react-d3-tree';
import Close from '@apple/symbols/light/xmark.svg';
import styles from './sass/tech-specs-collection-graph-view.scss';
import {getDepthGraphViewDropdownOptions} from './helpers/treeDepthUtils';
import {normalizeNode} from './helpers/treeDataUtils';

const hasChildren = node => node?.children?.length > 0;

const hasGroupedTechSpecsBranch = node => {
if (!node) return false;
if ((node.name === 'Tech Specs' || node.specLabelParent === 'Tech Specs') && hasChildren(node)) return true;
return node.children?.some(child => hasGroupedTechSpecsBranch(child)) || false;
};

function TechSpecsCollectionGraphViewWithGroupings({
groups,
optionValuesMap,
sortTemplateMap,
part,
detailLists,
query,
optionGroupDimensions,
optionGroupVariantValues,
setSelectedNode,
selectedNode,
graphViewInitialDepth,
setDepthGraphViewDropdownOptions,
onOptionGroupTypeClick,
onTechSpecsClick,
CollectionSchemaParts,
optionGroupAttributesMap,
techSpecs,
graphViewType,
withGroupings = false,
treeData,
wrapText,
getNodeShape
}) {
const {revision, segment, language, geo, channel} = query;
const treeContainer = useRef(null);
const history = useHistory();
const [showImageModal, setShowImageModal] = useState({
show: false,
imageUrl: ''
});
const [showOptionModal, setShowOptionModal] = useState({
show: false,
sapContainerParts: [],
optionValueAttributes: {}
});
const clickTimeoutRef = useRef(null);
const [translate, setTranslate] = useState({x: 0, y: 0});
const [rightTreeData, setRightTreeData] = useState({});
const [showTree, setShowTree] = useState(false);
const [progressiveDepth, setProgressiveDepth] = useState(1);
const [isRenderingTree, setIsRenderingTree] = useState(false);

useEffect(
() => {
const depthOptions = getDepthGraphViewDropdownOptions(treeData);
setDepthGraphViewDropdownOptions(depthOptions);

const updateTranslate = () => {
if (treeContainer.current) {
const rect = treeContainer.current.getBoundingClientRect();
setTranslate({x: rect.width / 2, y: rect.height / 4});
}
};

const handleResize = throttle(updateTranslate, 250);
updateTranslate();

window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
},
[treeData]
);

useEffect(
() => {
setShowTree(false);
setProgressiveDepth(1);
setIsRenderingTree(true);

const startRender = () => {
setShowTree(true);
setIsRenderingTree(false);

let current = 1;
const targetDepth = Number(graphViewInitialDepth) || 3;

const expandNext = () => {
if (current < targetDepth) {
current++;
setProgressiveDepth(current);
if ('requestIdleCallback' in window) {
window.requestIdleCallback(expandNext, {timeout: 200});
} else {
setTimeout(expandNext, 80);
}
}
};

if ('requestIdleCallback' in window) {
window.requestIdleCallback(expandNext, {timeout: 300});
} else {
setTimeout(expandNext, 100);
}
};
const id =
'requestIdleCallback' in window
? window.requestIdleCallback(startRender, {timeout: 300})
: setTimeout(startRender, 150);

return () => {
if ('cancelIdleCallback' in window) cancelIdleCallback(id);
clearTimeout(id);
};
},
[treeData, graphViewType, graphViewInitialDepth, withGroupings]
);

useEffect(
() => {
setProgressiveDepth(Number(graphViewInitialDepth) || 1);
},
[graphViewInitialDepth]
);

useEffect(
() => {
if (selectedNode) {
const displayAarr = Object.entries(detailLists[selectedNode] ?? {}).map(entry => {
const [key, value] = entry;
const dimensions = key.split('.').slice(2);
const dimensionArr = optionGroupDimensions[selectedNode];
const variants = dimensionArr?.map((dim, ind) => [dim, dimensions[ind]]);
const searchString = queryString.stringify({
optionGroupType: selectedNode,
revision,
geo,
segment,
language,
channel,
variants
});

return {
name: key,
value: value,
isLink: true,
linkTo: `/option-value-display-name-setup?${searchString}`
};
});

const optionAarr =
optionValuesMap[selectedNode]?.map(ov => ({
name: ov.optionPartNumber,
value: ov.optionDisplay,
onClick: () => {
setShowOptionModal({
show: true,
sapContainerParts: ov.sapContainerParts,
optionValueAttributes: ov.optionValueAttributes
});
}
})) ?? [];

const groupValueAarr = map(optionGroupVariantValues[selectedNode], (variantVal, variantType) => {
const variantAarr = variantVal?.map(val => val);
return {
name: `${variantType} (${variantAarr.length})`,
children: variantAarr
};
});

const optionGroupAttributesAarr = !isEmpty(optionGroupAttributesMap[selectedNode])
? Object.entries(optionGroupAttributesMap[selectedNode]).map(([key, value]) => ({
name: key,
value: value
}))
: [];

const imageAarr = CollectionSchemaParts?.imageDetails?.imageInfo.map(imageValue => ({
name: imageValue.imageKey,
value: imageValue.baseImageName,
onClick: () => {
setShowImageModal({
show: true,
imageUrl: imageValue.imageUrl
});
}
}));

const sortTemplateAarr = sortTemplateMap[selectedNode]
? [
{
name: sortTemplateMap[selectedNode].templateName,
isLink: true,
linkTo: `/option-sort-templates/create?templateId=${sortTemplateMap[selectedNode].templateId}`
}
]
: [];

const data = {
name: selectedNode,
children: []
};

if (displayAarr.length > 0) {
const displayNameArr = displayAarr.map(entry => ({
name: entry.name,
value: entry.value,
isLink: entry.isLink,
linkTo: entry.linkTo
}));
data.children.push({
name: `Display Name (${displayNameArr.length})`,
children: displayNameArr
});
}

if (optionAarr.length > 0) {
data.children.push({
name: `Option Values (${optionAarr.length})`,
children: optionAarr
});
}

if (groupValueAarr.length > 0) {
data.children.push({
name: `Variants (${groupValueAarr.length})`,
children: groupValueAarr
});
}

if (optionGroupAttributesAarr.length > 0) {
data.children.push({
name: 'Option Group Attributes',
children: optionGroupAttributesAarr
});
}

if (imageAarr.length > 0 && selectedNode === 'chassis') {
data.children.push({
name: 'Image Keys',
children: imageAarr
});
}

if (sortTemplateAarr.length) {
data.children.push({
name: 'Sort Template',
children: sortTemplateAarr
});
}
if (selectedNode === part) {
const collectionSchemaPartsAarr = [];

if (!isEmpty(CollectionSchemaParts?.btrParts)) {
collectionSchemaPartsAarr.push({
name: 'BTR Parts',
children: CollectionSchemaParts.btrParts.map(btrPart => ({name: btrPart, value: null}))
});
}

if (!isEmpty(CollectionSchemaParts?.containerParts)) {
collectionSchemaPartsAarr.push({
name: 'Container Parts',
children: CollectionSchemaParts.containerParts.map(containerPart => ({name: containerPart, value: null}))
});
}

if (!isEmpty(CollectionSchemaParts?.imageDetails?.imageInfo)) {
collectionSchemaPartsAarr.push({
name: 'Image Keys',
children: CollectionSchemaParts.imageDetails.imageInfo.map(imageValue => ({
name: imageValue.imageKey,
value: imageValue.baseImageName,
onClick: () => {
setShowImageModal({
show: true,
imageUrl: imageValue.imageUrl
});
}
}))
});
}

if (collectionSchemaPartsAarr.length > 0) {
data.children.push(...collectionSchemaPartsAarr);
}
}

setRightTreeData(data);
}
},
[
selectedNode,
detailLists,
optionValuesMap,
optionGroupVariantValues,
sortTemplateMap,
CollectionSchemaParts,
optionGroupDimensions
]
);

const renderTree = (node, depth = 0) => {
const hasChildren = node.children && node.children.length > 0;
const nodeClass = () => {
if (node.onClick) return 'global-link-medium';
else if (node.isLink) return 'global-link-medium link';
else return 'global-body-medium';
};
const handleClick = () => {
if (node.onClick) {
node.onClick();
return;
}
if (node.isLink && node.linkTo) {
history.push(node.linkTo);
}
};
return (
<div className="tree-node-wrapper">
<div className="tree-node-line-wrapper">{depth > 0 && <div className="vertical-line" />}</div>
<div key={node.name + depth} className="tree-node">
<div>
<span className="node-circle" />
<span className={`tree-head ${nodeClass()}`} onClick={handleClick}>
{node.name}
</span>
<span className="tree-value global-body-medium"> {node.value}</span>
</div>
{hasChildren && (
<div className="tree-children">
{node.children.map((child, idx) => (
<div className="tree-child-connector" key={child.name + idx}>
<div className="horizontal-line" />
{renderTree(child, depth + 1)}
</div>
))}
</div>
)}
</div>
</div>
);
};

const renderCustomNode = ({nodeDatum, toggleNode}) => {
const containsDetail =
!isEmpty(detailLists[nodeDatum.name]) ||
!isEmpty(optionValuesMap[nodeDatum.name]) ||
!isEmpty(optionGroupVariantValues[nodeDatum.name]) ||
!isEmpty(sortTemplateMap[nodeDatum.name]);

const isSelected = selectedNode === nodeDatum.name;

const handleClick = () => {
if (nodeDatum.name === part) {
clickTimeoutRef.current = setTimeout(() => {
setSelectedNode(nodeDatum.name);
clickTimeoutRef.current = null;
}, 250);
} else {
if (clickTimeoutRef.current) {
if (containsDetail) {
setSelectedNode(nodeDatum.name);
}
clearTimeout(clickTimeoutRef.current);
clickTimeoutRef.current = null;
} else {
clickTimeoutRef.current = setTimeout(() => {
toggleNode();
clickTimeoutRef.current = null;
}, 250);
}
}
};

const setRef = el => {
if (!el) return;

el.ondblclick = null;

el.ondblclick = e => {
e.stopPropagation();
if (nodeDatum?.isSpecLabelNode === true && nodeDatum?.specLabelParent === 'Tech Specs') {
const selectedGroup = techSpecs.find(grp => grp.specLabel === nodeDatum.name);
if (selectedGroup) onTechSpecsClick(selectedGroup);
}
};
};

const shape = getNodeShape(nodeDatum, isSelected, withGroupings, part);
const wrappedLines = wrapText(nodeDatum.name, 14);
const lineHeight = 16;
const totalHeight = wrappedLines.length * lineHeight;
const startY = -(totalHeight / 2) + lineHeight / 2;
const isTopLevelGrouped = nodeDatum.children && nodeDatum.children.length > 0 && nodeDatum.__rd3t.depth === 3;
return (
<g onClick={handleClick} ref={setRef}>
{shape}
{nodeDatum.type !== 'variantKeyGroup' && (
<>
<text fill="black" textAnchor="middle" fontSize="12" fontWeight="400">
{wrappedLines.map((line, i) => (
<tspan key={i} x="0" y={startY + i * lineHeight}>
{line}
</tspan>
))}
</text>
{isTopLevelGrouped &&
withGroupings && (
<text fill="black" textAnchor="middle" fontSize="10" fontWeight="500" dy={wrappedLines.length * 12}>
(Grouped)
</text>
)}
</>
)}
</g>
);
};

const pathClassFunc = React.useCallback(linkData => {
const target = linkData?.target?.data ?? {};
const isGroupedTechSpecsBranch = hasGroupedTechSpecsBranch(target);
return isGroupedTechSpecsBranch ? 'link-grouped' : 'link-default';
}, []);

const summaryNode = () => {
const totalOptionValues = optionValuesMap[selectedNode]?.length || 0;
const totalVariants = Object.keys(optionGroupVariantValues[selectedNode] || {}).length;
const totalVariantValues = Object.values(optionGroupVariantValues[selectedNode] || {}).reduce(
(acc, arr) => arr.length + acc,
0
);
const totalComponentVariations = totalVariants * totalVariantValues;
return (
<ul className="summary-node">
<li className="global-body-medium">
<strong>Total Option Values</strong>: {totalOptionValues}
</li>
<li className="global-body-medium">
<strong>Total Variants</strong>: {totalVariants}
</li>
<li className="global-body-medium">
<strong>Total Variant Values</strong>: {totalVariantValues}
</li>
<li className="global-body-medium">
<strong>Total Component Variations</strong>: {totalComponentVariations}
</li>
</ul>
);
};
const data = normalizeNode(rightTreeData);
return (
<div className={styles.graphView}>
<div className={`${styles.tree} ${!isEmpty(selectedNode) ? 'sel-node' : ''}`} ref={treeContainer}>
{isRenderingTree ? (
<div className="loader" />
) : showTree ? (
<Tree
key={`${graphViewType}-${withGroupings}-${showTree ? graphViewInitialDepth : progressiveDepth}`}
data={treeData}
orientation={graphViewType}
translate={translate}
pathFunc="diagonal"
nodeSize={graphViewType === 'horizontal' ? {x: 320, y: 160} : {x: 160, y: 280}}
separation={{siblings: 1.5, nonSiblings: 2}}
renderCustomNodeElement={renderCustomNode}
enableLegacyTransitions={false}
zoomable={true}
collapsible={true}
transitionDuration={0}
initialDepth={Number(graphViewInitialDepth) || progressiveDepth}
pathClassFunc={pathClassFunc}
/>
) : (
<div className="loader" />
)}
</div>
<div className={`${styles.legendPanel} ${!isEmpty(selectedNode) ? 'has-selected-node' : ''}`}>
{withGroupings ? (
<ul className="legend-list">
<li>
<span className="legend-shape circle collection" /> Collection
</li>
<li>
<span className="legend-shape circle optiongrouptype" /> Option Group Type
</li>
<li>
<span className="legend-shape circle category" /> TS Category
</li>
<li>
<span className="legend-shape rect property" /> TS Category Variants
</li>
<li>
<span className="legend-shape rect variant" /> Valid Variant Combinations
</li>
<li>
<span className="legend-shape rect combination" /> Collection Variants
</li>
<li>
<span className="legend-shape rect keys" />
<div>
<span>TS Category Property Keys</span>
<br />
<span className="sub-text"> (TS Category Property : Comparison Value)</span>
</div>
</li>
<li>
<span className="legend-shape rect localized" /> Localized Copy
</li>
</ul>
) : (
<ul className="legend-list">
<li>
<span className="legend-shape circle collection" /> Collection
</li>
<li>
<span className="legend-shape circle optiongrouptype" /> Option Group Type
</li>
<li>
<span className="legend-shape circle category" /> TS Category
</li>
<li>
<span className="legend-shape circle combination" /> Collection Variants
</li>
<li>
<span className="legend-shape circle keys" /> TS Category Variants
</li>
</ul>
)}
</div>

{selectedNode && (
<div className={styles.rightPanel}>
<button
className="close-btn"
onClick={() => {
setSelectedNode(null);
}}
>
<Close className="close-icon" desiredFontSize={12} />
</button>
<div className="data-wrapper">
<button
className="global-header-super"
onClick={() => {
const selectedGroup = groups.find(group => {
return group.optionGroupType === selectedNode;
});
onOptionGroupTypeClick(selectedGroup);
}}
>
{selectedNode}
</button>
{summaryNode()}
<hr />
{data ? renderTree(data) : <div className="global-body-medium">No Results</div>}
</div>
</div>
)}

<ConfirmModal
{...{
show: showImageModal?.show,
onHide: () =>
setShowImageModal({
show: false
}),
onConfirm: () =>
setShowImageModal({
show: false
}),
confirmButtonText: 'Close',
showCancelButton: false
}}
>
{showImageModal.imageUrl && (
<img
className={`${styles.imageViewModal}`}
src={showImageModal.imageUrl}
onError={event => (event.target.style.display = 'none')}
/>
)}
</ConfirmModal>
<ConfirmModal
{...{
show: showOptionModal?.show,
onHide: () =>
setShowOptionModal({
show: false
}),
onConfirm: () =>
setShowOptionModal({
show: false
}),
confirmButtonText: 'Close',
showCancelButton: false
}}
>
<div className={styles.graphViewModal}>
<div className="data-wrapper">
{showOptionModal.sapContainerParts && (
<details className="details-wrapper" open={true}>
<summary className="detail-title">
<span className="section-title global-header-medium">Sap Container Parts</span>
</summary>
<div className="data-detail">
<div className="global-body-medium">{showOptionModal.sapContainerParts.join(' ,')}</div>
</div>
</details>
)}
{showOptionModal.optionValueAttributes && (
<details className="details-wrapper" open={true}>
<summary className="detail-title">
<span className="section-title global-header-medium">Option Value Attributes</span>
</summary>
<div className="data-detail">
{Object.entries(showOptionModal.optionValueAttributes).map(([key, value], index) => (
<div className="option-value-row global-body-medium" key={index}>
<div className="value">{key}</div>
<div className="name">{value}</div>
</div>
))}
</div>
</details>
)}
</div>
</div>
</ConfirmModal>
</div>
);
}

export default TechSpecsCollectionGraphViewWithGroupings;
@import '@aosprodsys/brucke-ui-core/dist/es/theme/variables.scss';
@import '/client/sass/pages/vars';

:local(.graphView) {
display: flex;
width: 100%;
border-top: 1px solid #d2d2d7;
position: relative;

@include mobile {
height: 100%;
border: 1px solid $fill-gray-tertiary;
border-radius: 12px;
}
}

:local(.tree) {
width: 100%;
height: calc(100vh - 15rem);
margin: 0 auto;
overflow: hidden;
position: relative;
&.sel-node {
@include mobile {
width: 10%;
}
}
@include mobile {
padding: 0px;
height: 100%;
}

.rd3t-tree-container {
width: 100%;
height: 100%;

.rd3t-node {
stroke: none;

&:hover {
cursor: pointer;
}
}

.rd3t-link {
fill: none;
stroke: #888;
stroke-width: 2px;
}

.rd3t-leaf-node {
stroke: none;
}
}
.rd3t-link.link-grouped {
stroke: #f39c12;
stroke-width: 2px;
stroke-opacity: 0.95;
}

.rd3t-link.link-default {
stroke: #b0b0b0;
stroke-width: 1.5px;
stroke-opacity: 0.8;
}
}

:local(.graphViewModal) {
.data-wrapper {
margin: 20px 12px;

.button-header {
margin-bottom: 20px;
}
summary {
display: list-item;
&:focus {
outline: none;
}
cursor: pointer;
}

.detail-row {
margin: 5px 0px;

.link {
margin-right: 10px;
}
}
.data-detail {
padding-left: 26px;
}
.option-value-row {
display: grid;
grid-template-columns: 120px 1fr;
}
}
}

:local(.rightPanel) {
width: 40%;
border-left: 2px solid #d2d2d7;
margin-top: -145px;
overflow-y: auto;
overscroll-behavior: contain;
height: calc(100vh - 44px - 105px + 40px);
background-color: white;
@include mobile {
position: absolute;
margin: 0;
right: 0;
top: 100px;
height: 100vh;
width: 80%;
}

.hr {
margin-bottom: 0px;
}

.close-btn {
cursor: pointer;
position: absolute;
right: 0px;
margin: 12px 5px;
@include mobile {
right: 10px;
}
.close-icon {
vertical-align: middle;
}
}

.data-wrapper {
margin: 20px 12px;
}

.summary-node {
margin-top: 5px;
list-style: none;
margin-left: 0px;

&.link {
text-decoration: underline;
}
}

.tree-node-wrapper {
position: relative;
}

.vertical-line {
position: absolute;
top: -6px;
left: -26px;
width: 2px;
height: 100%;
background-color: #d2d2d7;
z-index: 0;
}

.horizontal-line {
position: absolute;
top: 18px;
left: 5px;
width: 25px;
height: 2px;
background-color: #d2d2d7;
z-index: 0;
}

.tree-node {
position: relative;
padding-top: 5px;
padding-bottom: 10px;
}

.tree-child-connector {
position: relative;
padding-left: 30px;
}

.tree-head {
display: inline-block;
width: 200px;
word-break: break-word;

&.link {
text-decoration: underline;
}
}

.tree-value {
display: inline-block;
width: calc(100% - 260px);
word-break: break-word;
vertical-align: top;
margin-left: 20px;
@include mobile {
display: flex;
width: auto;
}
}

.node-circle {
display: inline-block;
width: 10px;
height: 10px;
vertical-align: top;
border-radius: 50%;
margin-right: 8px;
margin-top: 9px;
flex-shrink: 0;
background-color: #86868b;
border: 2px solid #86868b;
}

.global-header-super {
color: #06c;
}

.global-link-medium {
cursor: pointer;
}
}

:local(.imageViewModal) {
max-width: 300px;
}

:local(.legendPanel) {
flex-shrink: 0;
align-self: flex-start;
margin-left: 12px;
margin-top: 8px;
background-color: #fff;
border: 1px solid #d2d2d7;
border-radius: 8px;
padding: 12px 16px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
height: fit-content;
margin-right: 8px;
position: absolute;
right: 0;
z-index: 2;

&.has-selected-node {
position: absolute;
right: 30%;
top: 0;
}

@include mobile {
margin-top: 10px;
margin-left: 0;
width: 100%;
}

h4 {
margin-bottom: 8px;
font-weight: 600;
color: #333;
}

.legend-list {
list-style: none;
padding: 0;
margin: 0;

li {
display: flex;
align-items: center;
margin-bottom: 8px;
font-size: 13px;
color: #333;
background: linear-gradient(145deg, #f9f9f9, #ffffff);
border-radius: 6px;
padding: 6px 10px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease-in-out;

&:hover {
transform: translateY(-2px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.18);
}
}
.sub-text {
font-size: 11px;
color: #444;
display: inline-block;
}

.legend-shape {
width: 16px;
height: 16px;
margin-right: 8px;
border: 1.5px solid #888;
flex-shrink: 0;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

.circle {
border-radius: 50%;
}

.rect {
border-radius: 3px;
width: 18px;
height: 10px;
}

.collection {
background-color: #a9cbcf;
border-color: #005e8c;
}

.optiongrouptype {
background-color: #7ecfed;
border-color: #0288d1;
}

.category {
background-color: #ffe082;
border-color: #f9a825;
}

.property {
background-color: #e1bee7;
border-color: #8e24aa;
}

.variant {
background-color: #c8e6c9;
border-color: #2e7d32;
}

.combination {
background-color: #ff7043;
border-color: #bf360c;
}

.keys {
background-color: #e1f5fe;
border-color: #0288d1;
}

.localized {
background-color: #fff3e0;
border-color: #f57c00;
}
}
}
import {useScreen} from '../utils/screen';
const {isMobile} = useScreen();
import React, {useState, useEffect} from 'react';

export const useScreen = () => {
const [screen, setScreen] = useState({
isMobile: window.innerWidth <= 768
});
const handleResize = () => {
setScreen({isMobile: window.innerWidth <= 768});
};

useEffect(() => {
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);

return screen;
};

export const withScreen = Component => props => {
const screen = useScreen();
return <Component {...props} screen={screen} />;
};
we should show legend in mobile like iocn like a tooltiop
     
 
what is notes.io
 

Notes is a web-based application for online taking notes. You can take your notes and share with others people. If you like taking long notes, notes.io is designed for you. To date, over 8,000,000,000+ notes created and continuing...

With notes.io;

  • * You can take a note from anywhere and any device with internet connection.
  • * You can share the notes in social platforms (YouTube, Facebook, Twitter, instagram etc.).
  • * You can quickly share your contents without website, blog and e-mail.
  • * You don't need to create any Account to share a note. As you wish you can use quick, easy and best shortened notes with sms, websites, e-mail, or messaging services (WhatsApp, iMessage, Telegram, Signal).
  • * Notes.io has fabulous infrastructure design for a short link and allows you to share the note as an easy and understandable link.

Fast: Notes.io is built for speed and performance. You can take a notes quickly and browse your archive.

Easy: Notes.io doesn’t require installation. Just write and share note!

Short: Notes.io’s url just 8 character. You’ll get shorten link of your note when you want to share. (Ex: notes.io/q )

Free: Notes.io works for 14 years and has been free since the day it was started.


You immediately create your first note and start sharing with the ones you wish. If you want to contact us, you can use the following communication channels;


Email: [email protected]

Twitter: http://twitter.com/notesio

Instagram: http://instagram.com/notes.io

Facebook: http://facebook.com/notesio



Regards;
Notes.io Team

     
 
Shortened Note Link
 
 
Looding Image
 
     
 
Long File
 
 

For written notes was greater than 18KB Unable to shorten.

To be smaller than 18KB, please organize your notes, or sign in.